目录
1\ CONDITIONAL
Given four unsigned numbers, find the minimum. Unsigned numbers can be compared with standard comparison operators (a < b). Use the conditional operator to make two-way min circuits, then compose a few of them to create a 4-way min circuit. You'll probably want some wire vectors for the intermediate results.
HINT:
Verilog中的三目运算符的用法:
verilog中有跟c语言类似的三目运算符,这可以用于在一行中根据条件选择两个值中的一个,而不用在组合always块中使用if-then。下面给出一些示例:
(0 ? 3 : 5) // This is 5 because the condition is false.
(sel ? b : a) // A 2-to-1 multiplexer between a and b selected by sel.
always @(posedge clk) // A T-flip-flop.
q <= toggle ? ~q : q;
always @(*) // State transition logic for a one-input FSM
case (state)
A: next = w ? B : A;
B: next = w ? A : B;
endcase
assign out = ena ? q : 1'bz; // A tri-state buffer
((sel[1:0] == 2'h0) ? a : // A 3-to-1 mux
(sel[1:0] == 2'h1) ? b :
c )
CORRECT1:将case语句和三目运算符进行配合使用,我这里的三目运算符是嵌套使用的。
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);
// assign intermediate_result1 = compare? true: false;
always @(*)
begin
case(a>b)
1:min=(b>c)?((c>d)?d:c):((b>d)?d:b);
0:min=(a>c)?((c>d)?d:c):((a>d)?d:a);
endcase
end
endmodule
CORRECT2:
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);
wire [7:0] mintemp1;
wire [7:0] mintemp2;
assign mintemp1 = (a<b)? a:b;
assign mintemp2 = (c<mintemp1)?c:mintemp1;
assign min = (d<mintemp2)?d:mintemp2;
endmodule
2\ REDUCTION
Parity checking is often used as a simple method of detecting errors when transmitting data through an imperfect channel. Create a circuit that will compute a parity bit for a 8-bit byte (which will add a 9th bit to the byte). We will use "even" parity, where the parity bit is just the XOR of all 8 data bits.(奇偶校验经常被用作传输数据时检测错误的简单方法。创建一个电路,为一个8位字节计算一个奇偶校验位(它将在1个字节的基础上增加1位)。如果是偶校验,只要将该8位数据第一位和第二位进行异或,然后将得到的结果和第三位异或,依次下去,直到和第七位异或,
这样得到的最后结果,就是偶校验位;如果是奇校验,将上面的偶校验位取反即可。)
DEVELOPMENT(拓展):
其实在verilog中,与,或,非,异或,同或,这5个运算符,除了非以外,其他4个都是二目运算符,也即是需要两个操作数,然后中间加上相应的运算符。
但是这里有一种称作,一元约减运算符,就是说,与,或,异或,同或,这4个运算符可以作为一元约减运算符,也即是只需要一个操作数,例如:even_bit = ^a;
不过运算的过程,和二目运算符不同了,一元约减运算符的运算过程是:首先将操作数 的第一位和第二位进行与、或、非运算;然后再将运算结果和第三位进行与、或、非运算; 依次类推直至最后一位。
就是说,assign even_bit = ^a;等效于assign even_bit=a[0]^a[1]^a[2]^a[3]^a[4]^a[5]^a[6]^a[7];
最后提到一下C语言 &=,^=,|=,运算符,例如:b^=a;等效于b=b^a;这个和上面提到的,不是同一个东西,注意不要混淆。我第一次看到那个语句的时候,就是利用C语言的方式来理解的,百思不得其解。
HINT:
缩位运算符:
缩位运算符可以对向量的各个位进行和、或和异或操作,产生1位的输出:
& a[3:0] // AND : a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0] // OR : b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0] // XOR : c[2]^c[1]^c[0]
CORRECT:
module top_module (
input [7:0] in,
output parity);
assign parity = ^in;
endmodule
3\ VECTOR100R
Given a 100-bit input vector [99:0], reverse its bit ordering.
DEVELOPMENT:
verilog循环语句:
(1)while循环
格式:
while (condition) begin
…
end
while 循环中止条件为 condition 为假。如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。当然,执行语句只有一条时,关键字 begin 与 end 可以省略。
下面代码执行时,counter 执行了 11 次。
`timescale 1ns/1ns
module test ;
reg [3:0] counter ;
initial begin
counter = 'b0 ;
while (counter<=10) begin
#10 ;
counter = counter + 1'b1 ;
end
end
//stop the simulation
always begin
#10 ; if ($time >= 1000) $finish ;
end
endmodule
注意:
当我使用while作为循环编程时,有时会弹出:
“Error (10119): Verilog HDL Loop Statement error at top_module.v(16): loop with non-constant loop condition must terminate within 250 iterations File: /home/h/work/hdlbits.7268514/top_module.v Line: 16”
意思是:“非常量循环条件的循环必须在250次迭代内终止”
这里的话有两个解决方法:
方法一:修改编译器默认循环上限。
在英特尔官网上有给出该错误的解决方案,即在工程的.qsf文件中添加
set_global_assignment -name VERILOG_NON_CONSTANT_LOOP_LIMIT 300。
此时循环次数上限修改为 300,实测最大循环上限为 5000,这是很多Verilog教材中没有提到的。
方法二:当然就是换一个循环语句了,用for之类的循环不香么。
(2)for循环
格式:
for(initial_assignment; condition ; step_assignment) begin
…
end
initial_assignment 为初始条件。condition 为终止条件,condition 为假时,立即跳出循环。
step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。
一般来说,因为初始条件和自加操作等过程都已经包含在 for 循环中,所以 for 循环写法比 while 更为紧凑,但也不是所有的情况下都能使用 for 循环来代替 while 循环。
下面 for 循环的例子,实现了与 while 循环中例子一样的效果。需要注意的是,i = i + 1 不能像 C 语言那样写成 i++ 的形式,i = i -1 也不能写成 i -- 的形式。
// for 循环语句
integer i ;
reg [3:0] counter2 ;
initial begin
counter2 = 'b0 ;
for (i=0; i<=10; i=i+1) begin
#10 ;
counter2 = counter2 + 1'b1 ;
end
end
(3)repeat循环
格式:
repeat (loop_times) begin
…
end
repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。
下面 repeat 循环例子,实现了与 while 循环中的例子一样的效果。
// repeat 循环语句
reg [3:0] counter3 ;
initial begin
counter3 = 'b0 ;
repeat (11) begin //重复11次
#10 ;
counter3 = counter3 + 1'b1 ;
end
end
(4)forever循环
格式:
forever begin
…
end
forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。
forever 相当于 while(1) 。
通常,forever 循环是和时序控制结构配合使用的。
例如,使用 forever 语句产生一个时钟:
reg clk ;
initial begin
clk = 0 ;
forever begin
clk = ~clk ;
#5 ;
end
end
例如,使用 forever 语句实现一个时钟边沿控制的寄存器间数据传输功能:
reg clk ;
reg data_in, data_temp ;
initial begin
forever @(posedge clk) data_temp = data_in ;
end
ERRO:
module top_module(
input [99:0] in,
output [99:0] out
);
always @(*) begin
for(i=0;i<100;i++) begin
assign out[i]=in[99-i];
end
end
endmodule
CORRECT:
module top_module(
input [99:0] in,
output [99:0] out
);
integer i;
always @(*) begin
for(i=0;i<100;i++) begin
out[i]=in[99-i];
end
end
endmodule
HINT:
always与assign不能共存,当你把assign写入always块里面时会报这个错误:
Error (10043): Verilog HDL unsupported feature error at top_module.v(8): Procedural Continuous Assignment to register is not supported File: /home/h/work/hdlbits.7267506/top_module.v Line: 8
意思是:Verilog HDL不支持的特性错误在top_module.v(8),不支持用于注册的过程连续赋值。
具体不能共存是因为:
1、被assign赋值的信号定义为wire型;被always(*)结构块的信号定义为reg型。类型不同不能共用。
2、另外一个区别则是更细微的差别:举个例子,
wire a;
reg b;
assign a = 1'b0;
always@(*)
b = 1'b0;
在这种情况下,做仿真时a将会正常为0, 但是b却是不定态。这是为什么?verilog规定,always@(*)中的*是指该always块内的所有输入信号的变化为敏感列表,也就是仿真时只有当always@(*)块内的输入信号产生变化,该块内描述的信号才会产生变化,而像always@(*) b = 1'b0;
这种写法由于1'b0一直没有变化,所以b的信号状态一直没有改变,由于b是组合逻辑输出,所以复位时没有明确的值(不定态),而又因为always@(*)块内没有敏感信号变化,因此b的信号状态一直保持为不定态。事实上该语句的综合结果有可能跟assign一样(本人没有去尝试),但是在功能仿真时就差之千里了。
4\ POPCOUNT255
A "population count" circuit counts the number of '1's in an input vector. Build a population count circuit for a 255-bit input vector.
ERRO:
module top_module(
input [254:0] in,
output [7:0] out );
integer i;
assign out=8'b00000000;
always @(*)
begin
while(i<255)
begin
if(in[i]==1)
out=out+1'b1;
else
out=out;
end
end
endmodule
CORRECT1:
module top_module(
input [254:0] in,
output [7:0] out );
integer i;
always @(*)
begin
out=8'b00000000;
for(i=0;i<255;i++)
begin
if(in[i]==1)
out=out+1'b1;
else
out=out;
end
end
endmodule
CORRECT2:
module top_module (
input [254:0] in,
output reg [7:0] out
);
always @(*) begin // Combinational always block
out = 0;
for (int i=0;i<255;i++)
out = out + in[i];//无非就是判断in,里面有几个一,有一的话直接当做技术信号好了。
end
endmodule
HINT:
So many things to add... How about a for loop?
使用循环的时候,如果循环次数大于250次的话,就别用while了,换一下;
值初始化的时候,如果有always块的话,就写在里面即可,别用assign写外面了,这样不能共存。
5\ ADDER100I
通过实例化100个全加法器来创建一个100位二进制脉动进位加法器。加法器将两个100位的数字和一个进位相加,得到一个100位的和并执行。为了鼓励您实际实例化全加法器,还需要输出脉动进位加法器中每个全加法器的进位。Cout[99]是最后一个满加法器的最后一个带出值,也是你经常看到的带出值。
HINT:
有许多完全加法器要实例化。这里可以使用实例数组或generate语句。
DEVELOPMENT:
参考:https://blog.csdn.net/weixin_45270982/article/details/115339274
generate语句:
1、介绍
generate生成语句可以动态的生成verilog代码,当对矢量中的多个位进行重复操作 时,或者当进行多个模块的实例引用的重复操作时,或者根据参数的定义来确定程序中是否应该包含某段Verilog代码的时候,使用生成语句能大大简化程序的编写过程。生成语句生成的实例范围,关键字generate-endgenerate用来指定该范围。生成实例可以是以下的一个或多个类型:模块、用户定义原语、门级语句、连续赋值语句、initial和always块。
2、用法
generate_for语句
(1)、必须使用genvar声明一个正整数变量,用作for循环的判断。(genvar是generate语句中的一种变量类型,用在generate_for中声明正整数变量,放在generate内外都可以。)
(2)、需要复制的语句必须写到begin_end语句里面。就算只有一句!!!!!!
(3)、begin_end需要有一个类似于模块名的名字。
例1:assign语句实现
module test(bin,gray);
parameter SIZE=8;
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
genvar i; //genvar i;也可以定义到generate语句里面
generate
for(i=0;i<SIZE;i=i+1)
begin:bit
assign bin[i]=^gray[SIZE-1:i];
end
endgenerate
endmodule
等同于下面语句
assign bin[0]=^gray[SIZE-1:0];
assign bin[1]=^gray[SIZE-1:1];
assign bin[2]=^gray[SIZE-1:2];
assign bin[3]=^gray[SIZE-1:3];
assign bin[4]=^gray[SIZE-1:4];
assign bin[5]=^gray[SIZE-1:5];
assign bin[6]=^gray[SIZE-1:6];
assign bin[7]=^gray[SIZE-1:7];
generate_if语句
generate_for用于复制模块,而generate_if则是根据模块的参数(必须是常量)作为条件判断,来产生满足条件的电路。相当于判断语句。
module generate_if(
input t0 ,
input t1 ,
input t2 ,
output d
);
localparam S = 6; //定义模块所需参数,用于判断产生电路
generate
if(S < 7)
assign d = t0 | t1 | t2;
else
assign d = t0 & t1 & t2;
endgenerate
endmodule
generate_case语句
generate_case其实跟generate_if一样,都是根据参数(都必须为常量)作为判断条件,来产生满足条件的电路,不同于使用了case语法而已。
module generate_case(
input t0 ,
input t1 ,
input t2 ,
output d
);
localparam S = 8; //定义模块所需参数,用于判断产生电路
generate
case(S)
0:assign d = t0 | t1 | t2;
1:assign d = t0 & t1 & t2;
default:
assign d = t0 & t1 | t2;
endcase
endgenerate
endmodule
ERRO:
module top_module(
input [99:0] a, b,
input cin,
output [99:0] cout,
output [99:0] sum );
generate
for(i=0;i<100;i=i+1)
begin:
assign {cout[i],sum[i]}=a[i]+b[i]+cout[i];
end
endgenerate
endmodule
CORRECT:
module top_module(
input [99:0] a, b,
input cin,
output [99:0] cout,
output [99:0] sum );
genvar i;
generate
for(i=0;i<100;i=i+1)
begin:ct
if(i!=0)
assign {cout[i],sum[i]}=a[i]+b[i]+cout[i-1];//需要加上前一位的进位,而不是加上本位的进位。
else
assign {cout[0],sum[0]}=a[0]+b[0]+cin;
end
endgenerate
endmodule
6\ BCDADD100
您将得到一个名为bcd_fadd的BCD一位数加法器,它将两个BCD数字和进位相加,并生成一个和进位。
module bcd_fadd ( input [3:0] a, input [3:0] b, input cin, output cout, output [3:0] sum );
实例化100个bcd_fadd,创建100位BCD脉冲进位加法器。你的加法器应该将两个100位的BCD数字(打包成400位向量)和一个进位相加,得到一个100位的和并进位。
本题示意图:
将上图所示的每个小bcd_fadd全部实例化即可。
ERRO:
module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
wire [99:0] t;
integer i;
always@(*)
begin
for(i=0;i<100;i++)
begin
if(i==0)
begin
bcd_fadd inst(a[3:0],b[3:0],cin,t[0],sum[3:0]);
end
else
begin
bcd_fadd inst(a[3+4*i:4*i],b[3+4*i:4*i],t[i-1],t[i],sum[3+4*i:4*i]);
end
end
cout=t[99];
end
endmodule
CORRECT:
module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
wire [99:0] t;
genvar i;
generate
for(i=0;i<100;i++)
begin:add
if(i==0)
begin
bcd_fadd inst(a[3:0],b[3:0],cin,t[0],sum[3:0]);
end
else
begin
bcd_fadd inst(a[3+4*i:4*i],b[3+4*i:4*i],t[i-1],t[i],sum[3+4*i:4*i]);
end
end
assign cout=t[99];
endgenerate
endmodule