数字电路设计主要就是,选择器、全加器、比较器,几个常用逻辑门,再加个D触发器,电路基本都能实现了。
组合逻辑+时序逻辑
组合逻辑用assign或always@(*)实现,
时序逻辑用always@(posedge clk or negedge rst_n)
有人说掌握Verilog 20%的语法就可以描述 90%以上的电路,说的对。
casez
1 always @(*)begin 2 casez(code) 3 8'b1???_???? : data[2:0] = 3'd7; 4 8'b01??_???? : data[2:0] = 3'd6; 5 8'b001?_???? : data[2:0] = 3'd5; 6 8'b0001_???? : data[2:0] = 3'd4; 7 8'b0000_1??? : data[2:0] = 3'd3; 8 8'b0000_01?? : data[2:0] = 3'd2; 9 8'b0000_001? : data[2:0] = 3'd1; 10 8'b0000_0001 : data[2:0] = 3'd0; 11 default : data[2:0] = 3'd0; 12 endcase 13 end
这样的case有优先级选择,虽然可综合,但是不推荐使用,有优先用if-else,没有直接用case。
synopsys的EDA工具有关于full case与parallel case可以查看下面博客链接。
https://blog.csdn.net/li_hu/article/details/10336511
generate+for
合理使用generate+for循环可以提高编码效率,同样的赋值语句需要赋值多次。
1 generate 2 genvar i; 3 for(i=0;i<16;i=i+1) 4 begin: neg_data 5 assign neg_data_out[i*DATA_WIDTH +:DATA_WIDTH] = 6 -data_in[i*DATA_WIDTH +:DATA_WIDTH] 7 end 8 endgenerate
同一个模块需要实例化多次
1 generate 2 genvar i; 3 for(i=0;i<16;i=i+1) 4 begin: mult_12x12 5 DW02_mult #( 6 .A_WIDTH(12), 7 .B_WIDTH(12) 8 ) u_DW02_mult0( 9 .A(mult_a[i*12 +:12]), 10 .B(mult_b[i*12 +:12]), 11 .TC(1'b0), 12 .PRODUCT(product[i*24 +:24]) 13 ); 14 end 15 endgenerate
当然这样写debug会有一些困扰,Verdi会显示每一个generate块,选中对应的块,加进去的波形就会是对应的bit信号。
generate if/case
做一些通用IP的方法,比如要做一个选择器通用IP,支持二选一,三选一,四选一。
1 generate if(MUX_NUM == 0)begin : mux4_1 2 always@(*)begin 3 case(sel[1:0]) 4 2'b00:data_out = data_in0; 5 2'b01:data_out = data_in1; 6 2'b10:data_out = data_in2; 7 default:data_out = data_in3; 8 endcase 9 end 10 end else if(MUX_NUM = 1) begin : mux3_1 11 always@(*)begin 12 case(sel[1:0]) 13 2'b00:data_out = data_in0; 14 2'b01:data_out = data_in1; 15 default:data_out = data_in2; 16 endcase 17 end 18 end else begin : mux2_1 19 always@(*)begin 20 case(sel[1:0]) 21 2'b00:data_out = data_in0; 22 default:data_out = data_in1; 23 endcase 24 end 25 end endgenerate
generate case可以写更多的分支
1 generate 2 case(MUX_NUM) 3 0:begin:mux_2 4 end 5 1:begin: mux_3 6 end 7 2:begin: mux_4 8 end 9 default:begin 10 end 11 endcase 12 end endgenerate
调用的时候只需要
1 mux #( 2 .MUX_NUM(0) 3 ) 4 u_mux( 5 ... 6 );
参数化定义
模块化设计,功能模块的划分尽可能细, 差别不大的代码通过参数化达到重复使用的目的。
1 always @(*)begin 2 case(sel) 3 CASE0:data_out = data_in0; 4 CASE1:data_out = data_in1; 5 CASE2。。。 6 default:; 7 endcase 8 end
实例化
1 mux #( 2 .CASE0(8'd11), 3 .CASE1(8'd44) 4 ... 5 ) 6 u_mux( 7 ... 8 );
移位操作
对于移位操作直接用位拼接,
1 assign data_shift[6:0] = data[4:0] << 2; 2 assign data_shift[7:0] = data[4:0] << shift[1:0];
写成
1 assign data_shift[6:0] = {data[4:0], 2'b0}; 2 always @(*)begin 3 case(shift[1:0]) 4 2'b00: data_shift[7:0] = {3'b0, data[4:0]}; 5 2'b01: data_shift[7:0] = {2'b0, data[4:0], 1'b0}; 6 2'b10: data_shift[7:0] = {1'b0, data[4:0], 2'b0}; 7 default:data_shift[7:0] = {data[4:0], 3'b0}; 8 endcase 9 end
如果是有符号数,高位要补符号位。也就是算术移位。
1 always @(*)begin 2 case(shift[1:0]) 3 2'b00: data_shift[7:0] = {{3{data[4]}}, data[4:0]}; 4 2'b01: data_shift[7:0] = {{2{data[4]}}, data[4:0], 1'b0}; 5 2'b10: data_shift[7:0] = {data[4], data[4:0], 2'b0}; 6 default:data_shift[7:0] = {data[4:0], 3'b0}; 7 endcase 8 end
shift也可能是有符号数,正数左移,负数右移。右移方法同理。
$clog2系统函数
Verilog-2005引入了$clog2系统函数,为了方便计算数据位宽,避免位浪费。这个其实是来凑数的。
1 parameter DATA_WIDTH = 4, 2 parameter CNT_WIDTH = log2(DATA_WIDTH) 3 parameter CNT_WIDTH = clog2(DATA_WIDTH-1) 4 parameter CNT_WIDTH = $clog2(DATA_WIDTH) 5 6 reg [DATA_WIDTH-1:0] data_r0; 7 8 reg [CNT_WIDTH-1:0] cnt; 9 10 //------------------------------------------------------- 11 //以下两个函数任用一个 12 //求2的对数函数 13 function integer log2; 14 input integer value; 15 begin 16 value = value-1; 17 for (log2=0; value>0; log2=log2+1) 18 value = value>>1; 19 end 20 endfunction 21 22 //求2的对数函数 23 function integer clogb2 (input integer bit_depth); 24 begin 25 for(clogb2=0; bit_depth>0; clogb2=clogb2+1) 26 bit_depth = bit_depth>>1; 27 end 28 endfunction
最后
欢迎指点,有哪些Verilog的奇技淫巧,留言告诉我吧。
转自:https://ninghechuan.com/2020/03/14/2020-3-14-Verilog%E5%87%A0%E4%B8%AA%E8%BF%99%E6%A0%B7%E7%9A%84%E5%86%99%E6%B3%95/