Verilog中的生成块的使用方法总结
生成语句可以动态地生成Verilog代码。这一声明语句方便了参数化模块的生成。当对矢量中的多个位进行重复操作时,或者当进行多个模块的实例引用的重复操作时,或者在根据参数的定义来确定程序中是否应该包括某段Verilog代码的时候,使用生成语句能够大大简化程序的编写过程。
生成语句能够控制变量的声明、任务或函数的调用,还能对实例引用进行全面的控制。编写代码时必须在模块中说明生成的实例范围,关键字 generate-endgenerate 用来指定该范围。
生成实例可以是以下的一个或多种类型∶
(1)模块;(2)用户定义原语;(3)门级原语(4)连续赋值语句;(5)initial和always块。
生成的声明和生成的实例能够在设计中被有条件地调用(实例引用)。在设计中可以多次调用(实例引用)生成的实例和生成的变量声明。生成的实例具有唯一的标识名,因此可以用层次命名规则引用。为了支持结构化的元件与过程块语句的相互联接,Verilog语言允许在生成范围内声明下列数据类型∶
(1)net(线网)、reg(寄存器);
(2)integer(整型数)、real(实型数)、time(时间型)、realtime(实数时间型);(3)event(事件)。
生成的数据类型具有唯一的标识名,可以被层次引用。此外,究竟是使用按照次序或者参数名赋值的参数重新定义,还是使用defparam 声明的参数重新定义,都可以在生成范围中定义。
注意∶生成范围中定义的defparam语句所能够重新定义的参数必须是在同一个生成范围内,或者是在生成范围的层次化实例当中。
任务和函数的声明也允许出现在生成范围之中,但是不能出现在循环生成当中。生成任务和函数同样具有唯一的标识符名称,可以被层次引用。
不允许出现在生成范围之中的模块项声明包括∶(1)参数、局部参数;
(2)输入、输出和输入/输出声明;(3)指定块。
生成模块实例的连接方法与常规模块实例相同。
在 Verilog 中有3种创建生成语句的方法,它们是;(1)循环生成;(2)条件生成;(3)case生成。
在5.7.1~5.7.2节中,将对这3种方法进行详细说明。
循环生成语句
循环生成语句允许使用者对下面的模块或模块项进行多次实例引用;(1)变量声明;(2)模块;
(3)用户定义原语、门级原语;(4)连续赋值语句;(5)initial和 always 块。
例 5.14】说明了如何使用生成语句对两个 N 位的总线用门级原语进行按位异或。在这里其目的在于说明循环生成语句的使用方法,其实这个例子如果使用矢量线网的逻辑表达式比用门级原语实现起来更为简单。
【例 5.14】 对两个N位总线变量进行按位异或。 //本模块生成两条 N位总线变量的按位异或 module bitwise_xor(out, iO, il);//参数声明语句,参数可以重新定义 parameter N= 32;//默认的总线位宽为32位 //端口声明语句 output[N-1:0] out; input [N-1:0] i0, il; //声明一个临时循环变量 //该变量只用于生成块的循环计算 //Verilog仿真时该变量在设计中并不存在 genvar j; //用一个单循环生成按位异或的异或门(xor) generate for(j=0; j<N; j=j+1) begin:xor_loop xor g1(out[j],i0[j],il[j]);//在生成块内部结束循环 end endgenerate //另外一种编写形式 //异或门可以用 always块来替代 // reg [N-1:0] out ; // generate // for(j=0; j<N; j=j+1) // begin:bit // always @(i0[j] or il[j]) out[i] = i0[j] - i[j]; // end // endgenerate endmodule
从【例5.14】中可以观察到下面几个有趣的现象;
(1)在仿真开始之前,仿真器会对生成块中的代码进行确立(展平),将生成块转换为展开的代码,然后对展开的代码进行仿真。因此,生成块的本质是使用循环内的一条语句来代替多条重复的 Verilog 语句,简化用户的编程。
(2)关键词genvar 用于声明生成变量,生成变量只能用在生成块之中;在确立后的仿真代码中,生成变量是不存在的。
(3)一个生成变量的值只能由循环生成语句来改变。
(4)循环生成语句可以嵌套使用,不过使用同一个生成变量作为索引的循环生成语句不能够相互嵌套。
(5)xor_loop 是赋予循环生成语句的名字,目的在于通过它对循环生成语句之中的变量进行层次化引用。因此,循环生成语句中各个异或门的相对层次名为∶xor_loop[0].gl,xor_loop[1].g1, …, xor_loop[31].gl。
循环生成语句的使用是相当灵活的。各种 Verilog 语法结构都可以用在循环生成语句之中。对于读者来说,重要的是能够想像出循环生成语句被展平之后的形式,这对于理解循环生成语句的作用是很有必要的。
【例5.15】给出了使用生成语句描述的脉动加法器,并且在循环生成语句中声明了线网变量。
【例5.15】 用循环生成语句描述的脉动加法器。 //本模块生成一个门级脉动加法器 module ripple_adder(co,sum,a0,al, ci); //参数声明语句,参数可以重新定义parameter N = 4;//默认的总线位宽为4 //端口声明语句 output [N-1∶0] sum; output co; input [N-1∶0] a0,al; input ci; //本地线网声明语句 wire [N-1:0 ] carry ; //指定进位变量的第0位等于进位的输入 assign carry [0]= ci ; //声明临时循环变量,该变量只用于生成块的计算 //由于在仿真前,循环生成已经展平,所以用Verilog 对设计进行仿真时,该变量已经不再存在 genvar i ; //用一个单循环生成按位异或门等逻辑 generate for(i= 0; i<N; i=i+1) begin∶ r_loop wire t1, t2, t3; xor gl(t1, a0[i], al[i]); xor g2(sum[i], t1, carry [i]); and g3(t2, a0Li], al[i]); and g4(t3, t1, carry [i]); or g5(carry [i+1], t2, t3); end //生成块内部循环的结束 endgenerate //生成块的结束 //根据上面的循环生成,Verilog编译器会自动生成以下相对层次实例名 // xor:r_loop[0].gl, r_loop[1].gl, r_loop[2].gl, r_loop[3].g1, // r_ioop[0].g2, r_loop[1].g2, r_loop[2].g2, r_loop[3].g2, // and:r.loop[0].g3, r_loop[1].g3, r.loop[2].g3, r_loop[3].g3, // r_loop[0].g4, r_loop[1].g4, r_loop[2].g4, r_loop[3].g4, //or: r_loop[0].g5, r_loop[1].g5, r_loop[2].g5, r_loop[3].g5 //根据上面生成的实例用下面这些生成的线网连接起来 // Nets:r_loop[0].t1, r.loop[0].t2, r_loop[0].t3 // r_loop[1].t1, r_loop[1].t2, r_loop[1].t3 // r_loop[2].t1, r_loop[2].t2, r_loop[2].t3 // r_loop[3].tl, r_loop[3].t2, r_loop[3].t3 assign co = carry[N]; endmodule
条件生成语句
条件生成语句类似于 if_else_if 的生成构造,该结构可以在设计模块中根据经过仔细推敲并确定表达式,有条件地调用(实例引用)以下这些 Verilog 结构;
(1)模块;
(2)用户定义原语、门级原语;
(3)连续赋值语句;
(4)initial或 always块。
【例5.16】说明如何用条件生成语句实现参数化乘法器。如果参数 a0_width 或 al_width 小于8(生成实例的条件),则调用(实例引用)超前进位乘法器;否则调用(实例引用)树形乘法器。
【例5.16】 使用条件生成语句实现参数化乘法器。 //本模块实现一个参数化乘法器 module multiplier(product,a0,al); //参数声明,该参数可以重新定义 parameter a0_width = 8; parametet al_width = 8; //本地参数声明 //本地参数不能用参数重新定义(defparam)修改 //也不能在实例引用时通过传递参数语句,即 #(参数1,参数 2,-…)的方法修改 localparam product_width = a0_width + al_width ; //端口声明语句 output [product_width-1:0] product; input [a0_width-1]:0] a0; input [al_width-1∶0] al; //有条件地调用(实例引用)不同类型的乘法器 //根据参数 a0_width和 al_width 的值,在调用时引用相对应的乘法器实例 generate if (a0_width<8) || (al_width<8) cal_multiplicr #(a0_width,al_width) m0(product,a0,al); else tree_multiplier #(a0_width,al_width) m0(produet,a0,al); endgenerate //生成块的结束 endmodule
case生成语句
case生成语句可以在设计模块中,根据仔细推敲确定多选一case 构造,有条件地调用(实例引用)下面这些Verilog 结构∶
(1)模块;
(2)用户定义原语、门级原语;
(3)连续赋值语句;
(4)initial 或 always 块。
【例 5.17】说明如何使用 case 生成语句实现 N位加法器。
【例5.17】 case 生成语句举例。 //本模块生成N位的加法器 module adder( co,sum,a0,al, ci); //参数声明,本参数可以重新定义 parameter N = 4; //缺省的总线位宽为4 //端口声明 output [N-1∶0] sum; output co; input [N-1:0] a0,al; input ci; //根据总线的位宽,调用(实例引用)相应的加法器 //参数N在调用(实例引用)时可以重新定义,调用(实例引用 //不同位宽的加法器是根据不同的 N 来决定的 generate case (N) //当 N=1或2时分别选用位宽为1位或2位的加法器 1∶adder_1bit adderl(co,sum,a0,al,ci);//1位的加法器 2∶adder_2bit adder2(co,sum,a0,al,ei);//2位的加法器 //默认的情况下选用位宽为 N 位的超前进位加法器 default: adder_cla #(N) adder3(co,sum,a0,al,ci); endcase endgenerate //生成块的结束 endrmodule
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架