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