FPGA基础设计(8)Verilog常数赋值、字符串、标识符
锡兰国阅读《IEEE Standard for Verilog 2005》时,做⼀些整理和记录。
1.整数赋值
按照Verilog 2005的标准:0-9、a-f、z、x称作数字位(digit);表⽰数字正负的’+‘和’-‘视作⼀元操作符(unary operator);常说的⼆进制、⼋进制、⼗进制、⼗六进制称作数字的基(ba);其在Verilog中的表⽰’b’、‘o’、‘d’、'h’称作基格式(ba format)字符;表⽰常数的bit数称作size。Verilog使⽤到的字符都是不区分⼤⼩写的。
另外要明⽩⼆进制数本⾝的运算规则与⼀个数是signed还是unsigned、⼩数点在哪个位置没有任何关系,这些只是设计者⼈为的规定。但是在代码设计时,要标识清楚数据是signed或unsigned,⽬的有两个:(1).对设计者的提⽰;(2).⼀些操作符、IP核的数据端⼝,对数据是signed或unsigned的“⾝份”有要求。
如果⼀个数是负数(前⾯加了’-’),那么它必定采⽤的是⼆进制补码形式。
⽅式1
尼格利陀人
常数赋值有两种,⼀种为直接赋值(符号+⼀串数字位),采⽤这种⽅式赋值时,默认数据是sigend形式。如下⾯⼀个简单的⽰例:
module test
(
input clk,
output reg [31:0] c
);
wire [31:0] a, b;
assign a =-595;
assign b =-10;
always @ (podge clk)
c <= a + b;
endmodule
对两个带符号数做加法运算,结果仍然是带符号数,Vivado中仿真时,将Radix设置为Signed Decimal,可以看到变量C的结果为-605。⽅式2
另⼀种则是常见的基常数赋值⽅法(符号+size+单引号+基格式字符+⼀串数字位),如"assign a = +16’d53;", 符号和size是可有可⽆的。
如果在基格式字符(b、o、d、h)前加上’s’,表⽰这是个带符号数signed;没有加’s’则表⽰是不带符号数unsigend。如果前⾯加了符号位“-”,会⾃动⽤⼆进制补码的形式。
如下⾯的简单⽰例:
`timescale 1ns /1ps
module test
(
input clk,
output reg [31:0] c
);
wire [31:0] a, b;
assign a =-32'd595;// 加上size,省去s
assign b =-'sd10;// 省略size,加上s
always @ (podge clk)
c <= a + b;
endmodule
仿真结果与第⼀个⽰例相同。
位宽扩展
这⾥先不讨论将低位宽的数据赋值给⾼位宽数据的问题,⽐如将12bit的a赋值给16bit的b。先讨论直接将低位宽的常数赋值给⾼位宽的数据时,位宽扩展遵循怎样的标准(不考虑x和z)。
以下⾯的测试代码为例,将12bit的常数赋值给16bit的数据:
wire [15:0] a =12'd565;
wire [15:0] b =-12'd565;
wire [15:0] c =-12'sd565;
reg [15:0] d =-12'sd565;
仿真结果如下,分别展⽰了赋值结果的2进制和10进制:
对于正数,赋值结果为⾼位补0。对于负数(⼆进制补码),⽆论是reg还是wire型、加s声明为带符号数或者不加s,结果都是⾼位补符号位。可见位宽扩展只与数据的正、负有关。Verilog HDL的位宽扩展机制可以确保设计者安全、便捷地完成程序设计。
“负数”与“带符号数”的区别
由于负数必须需要符号’-'来规定,所以第⼀感觉就是将负数和带符号数⼆者等同。其实两者并不完全相同,如果只加负号,不加s,虽然软件⼯具会将其⽤⼆进制补码表⽰,但仍不会将其当作带符号数signed看待。看下⾯的⽰例代码:
module test
(
input clk,
output reg [15:0] a,b,c,d
);
always @ (podge clk) begin
a <= -12 / 3;
b <= -'sd12 / 3;
c <= -'d12 / 3;
d <= -4'sd12 / 3;
end
endmodule
仿真结果如下图:
对于a:在没有规定⼤⼩和基格式时,-12和3都视作带符号数,因此结果为正确的-4;
对于b:-'sd12使⽤字符’s’声明这是个带符号数,结果同样是正确的-4;
对于c:-'d12虽然声明这是个负数,但没有加-s,软件仍然不会将其视作带符号数,运算结果与预期不符;
对于d:-4’sd12同时还指定了⼤⼩,但4’sd12已经超过了4bit带符号数的表⽰范围,它的值12(1100)实际意义上是-4(最⾼位符号位表⽰负数,⼆进制补码),前⾯再加’-'为正4,除以3,近似为整数后值为1。
2.实常数赋值
Verilog HDL本⾝是⽀持在代码中使⽤⼩数、科学计数法的,只不过赋值给整数型数据时,会发⽣隐式转换,转换为整数。看下⾯的⽰例:
wire [15:0] a =1e4;
wire [15:0] b =12.1;
wire [15:0] c =12.5;
reg [15:0] d =-12.5;
仿真结果如下:
数据a⽤科学计数法进⾏赋值。隐式转换的规则是:(1). 转换为最接近的整数;(2).中间值.5转换时,向远离0的⽅向进⾏。如数据c,12.5 => 13;数据d,-12.5 => -13。
3.字符串(String)
Verilog HDL同样也⽀持字符串的使⽤,使⽤双引号“”表⽰字符串内容,⼀个字符串必须放在⼀⾏内。在表达式和赋值语句中使⽤字符串,⼯具会将其视作⽆符号整数,⼀个字符对应⼀个8bit的ASCII码。
\n、\r、\t、\\和\"等常⽤的转义字符,Verilog HDL也同样⽀持。
下⾯是⼀个简单的测试代码:
module test
一语不发(
input clk,
output reg [8*4-1:0] c,
output [8*4-1:0] d
);
assign d ="CUIT";
always @ (podge clk)
c <="CUIT";
endmodule
仿真时可以看到,⽆论是reg还是wire类型,其存储的值本质都是“32’h43554954”,是⼀串ASCII码的组合。
设计仿真激励⽂件时,字符串更多是⼀种让仿真结果更直观的辅助⼿段(如⽤在$display系统任务中)。硬件设计中也有⼀定⽤处,如在⼀些通信协议的设计中,直接使⽤字符串操作会便捷很多,起码不⽤对着ASCII码表查找。
由于字符串的本质仍然是⽆符号整数,因此Verilog的各种操作运算对字符串也适⽤。⽐如⽤==和!=进⾏字符串的⽐较、⽤{ }完成字符串的拼接。不过需要注意:当字符串赋值的对象位宽较⼤时,左边会做补0处理,这样会对字符串的⽐较、拼接造成影响,结果可能与我们的预期不符,如下⾯⽰例代码:
output reg [7:0] a,b,c,d
);
reg [8*8-1:0] s1 = "Hello", s2 = " World!";
reg [8*5-1:0] s3 = "Hello";
reg [8*7-1:0] s4 = " World!";
always @ (podge clk) begin
if ({s1,s2} == "Hello World!")
我的百合网a <= 1'b1;
el a <= 1'b0;
if ({s3,s4} == "Hello World!")哺乳动物的特征
b <= 1'b1;
el b <= 1'b0;
end
endmodule
看起来s1和s2的拼接、s3和s4的拼接结果都应该是“Hello World!”,但仿真结果如下:
!
八平方事实上,s1和s2进⾏字符串赋值时先要⾼位补0,再拼接后的值已经不是单纯的“Hello Wrold!”。s3和s4的位宽正好与字符串长度相等,赋值时没有补0,因此⽐较结果相等。
ASCII码有个特殊的字符NUL,含义为空,码值为0。Verilog字符串中使⽤("")或("\0")都是表⽰NUL的含义,不会占据实际的存储空间。⽐如 reg [5*8-1:0] = “Hello\0”; 和 reg [5*8-1:0] = “Hello”; 的含义完全相同。
4.标识符(identifier)
常说的“数据名”、“变量名”,在标准中的规范名称应该是“identifier”。常见的标识符由字母、数字、$、下划线组成,其中数字和$不能是标识符的第⼀个字符。
标识符其实有“最⼤长度限制”,不同软件可以⾃⾏规定,不过这个限制值最⼩也有1024个字符。
这⾥专门提到标识符,是想记录⼀下⽐较陌⽣的转义标识符(escaped identifier),不过⼀般也⽤不到Verilog的这个特性。
⼀般的标识符对使⽤到的字符有限制,转义标识符可以使⽤所有可显⽰的ASCII字符。转字标识符必须以’\'为开头,以空格、Tab键盘、换⾏其中之⼀为结尾。
看下⾯的简单⽰例代码,注意表达式和赋值语句的分号前都加了空格:
output [23:0] c,
output d大麦种子
);
reg \75;
reg [23:0] \{a+b};
reg \always ;
reg \flag ;
always @ (podge clk) begin
中考高分作文
\75<=1'b1;
\{a+b}<=23'd565;
\always <=1'b1;
flag <=1'b0;
end
assign d = \75;
assign c = \{a+b};
endmodule
如果转义标识符中没有⽤到其它特殊字符,则本质上仍然是⼀般的标识符。上⾯的代码中,定义时为 \flag,使⽤时直接⽤ flag 即可。有个特殊情况,就是转义字符直接使⽤Verilog HDL中保留的关键字,如上⾯的 \always,不过使⽤时前⾯的’\'不能省略。