--- tags: RISCV title: 講義(verilog, 1.RTL 組合電路) --- # 設計方法 :::success Verilog 的設計多采用自上而下的設計方法(top-down)。即先定義頂層模塊功能,進而分析要構成頂層模塊的必要子模塊;然後進一步對各個模塊進行分解、設計,直到到達無法進一步分解的底層功能塊。這樣,可以把一個較大的系統,細化成多個小系統,從時間、工作量上分配給更多的人員去設計,從而提高了設計速度,縮短了開發週期。 ::: ![](https://i.imgur.com/IPVO52i.png) # 設計流程 ![](https://i.imgur.com/C1Djlh2.png) # verilog 資料類型(data type) 1. wire :::info wire 類型表示硬件單元之間的物理連線,由其連接的器件輸出端連續驅動。如果沒有驅動元件連接到 wire 型變量,預設值一般為 "Z" > verilog 數值種類 0(假), 1(真), x(未知), z(高阻) ::: ```verilog= wire interrupt ; wire flag1, flag2 ; wire gnd = 1'b0 ; ``` 2. reg :::info 寄存器(reg)用來表示存儲單元,它會保持數據原有的值,直到被改寫。 例如在 always 塊中,寄存器可能被綜合成邊沿觸發器,在組合邏輯中可能被綜合成 wire 型變量。寄存器不需要驅動源,也不一定需要時鐘信號。 ::: ```verilog= reg clk_temp; reg flag1, flag2 ; ``` 3. 向量 :::success 當寬度 > 1 時, reg, wire 就可以宣告為 向量的形式 ::: ```verilog= //example reg [3:0] counter; reg [0:31] data; //example reg [31:0] data1 ; reg [7:0] byte1 [3:0]; integer j ; always@* begin for (j=0; j<=3;j=j+1) begin byte1[j] = data1[(j+1)*8-1 : j*8]; //把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0] end end //example wire [31:0] temp1, temp2 ; assign temp1 = {byte1[0][7:0], data1[31:8]}; //数据拼接 assign temp2 = {32{1'b0}}; //赋值32位的数值0 ``` 4. 陣列 ```verilog= reg [3:0] counter [3:0] ; //由4個4bit計數器組成的數組 wire [7:0] addr_bus [3:0] ; //由4個8bit wire型變量組成的數組 wire data_bit[7:0][5:0] ; //聲明1bit wire型變量的二維數組 reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; //聲明4維的32bit數據變量數組 ``` example ```verilog= flag [1] = 32'd0 ; //將flag數組中第二個元素賦值為32bit的0值 counter[3] = 4'hF ; //將數組counter中第4個元素的值賦值為4bit 十六進制數F,等效於counter[3][3:0] = 4'hF,即可省略寬度; assign addr_bus[0] = 8'b0 ; //將數組addr_bus中第一個元素的值賦值為0 assign data_bit[0][1] = 1'b1; //將數組data_bit的第1行第2列的元素賦值為1,這裡不能省略第二個訪問標號,即 assign data_bit[0] = 1'b1; 是非法的。 data_4d[0][0][0][0][15:0] = 15'd3 ; //將數組data_4d中標號為[0][0][0][0]的寄存器單元的15~0bit賦值為3 ``` # 組合電路設計 ![](https://i.imgur.com/ylNYypf.png) > 這邊可以看到圖中,一個完整的電路會拆成循序邏輯電路一個是組合邏輯電路就是根據目前的input值產生對應的output值,之間不受clock影響,行為描述上用**always block** 或者是 **assign** 的方法都可以 ![](https://i.imgur.com/W5aS3sd.png) - always: ```verilog= reg c; reg x; always @(*) begin c = a + b; x = c – y; end ``` - assign: ```verilog= wire c; wire x; assign c = a + b; assign x = c – y; ``` ## example ![](https://i.imgur.com/SYlKGWj.png) - gate level ```verilog= nodule mux_case_4x1( a_i, b_i, c_i, d_i, sel_i, q_o ); input a_i; input b_i; input c_i; input d_i; input [1:0] sel_i; output q_o; not (); not (); and (); and (); and (); and (); or (q_o, ...); endmodule ``` ![](https://i.imgur.com/u9XDANO.png) - mux_case.v ```verilog= module mux_case ( a_i, b_i, c_i, d_i, sel_i, q_o ); input a_i; input b_i; input c_i; input d_i; input [1:0] sel_i; output q_o; reg q_o; always@(a_i or b_i or c_i or d_i or sel_i) begin case (sel_i) 2'b00 : q_o = a_i; 2'b01 : q_o = b_i; 2'b10 : q_o = c_i; default : q_o = d_i; endcase end endmodule ``` # 模擬 (使用 verilator) > step 1: 產生 mux_case.v 的 C++ class and head files ```bash= $ cat testbench.cpp #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "Vmux_case.h" #include "verilated_vcd_c.h" vluint64_t main_time = 0; double sc_time_stamp(int len) { return (main_time%len); } int main(int argc,char **argv) { Verilated::commandArgs(argc,argv); Verilated::traceEverOn(true); VerilatedVcdC* tfp = new VerilatedVcdC(); Vmux_case *top = new Vmux_case("top"); top->trace(tfp, 0); tfp->open("wave.vcd"); top->a_i = 0; top->b_i = 1; top->c_i = 0; top->d_i = 1; while(sc_time_stamp(20) < 19 && !Verilated::gotFinish()) { top->sel_i = rand() & 0x3; top->eval(); printf("time=%ld, a = %d, b = %d, c=%d, d=%d, sel_i=%d, q_o=%d\n",main_time, top->a_i, top->b_i, top->c_i, top->d_i, top->sel_i, top->q_o); tfp->dump(main_time); //dump wave main_time++; } top->final(); tfp->close(); delete top; return 0; } ``` > step 2: make and run ```bash $ verilator -Wall --cc --exe --build testbench.cpp mux_case.v --trace ``` ```bash $ make -C obj_dir -f Vmux_case.mk $ ./obj_dir/Vmux_case ``` > step 3: gtkwave wave.vcd ![](https://i.imgur.com/Kt9EvC1.png) # ROM 設計 ![](https://i.imgur.com/TH299hc.png) ```verilog= module rom #( parameter XLEN = 32, parameter MEM_SIZE = 32 ) ( input wire ce_i, input wire[XLEN-1:0] addr_i, output reg[XLEN-1:0] inst_o ); localparam MADDR_WIDTH = MEM_SIZE>1? clog2(MEM_SIZE) : 1; // read mem data initial begin $readmemh ("rom.data", mem); end reg[7:0] mem[MEM_SIZE-1:0]; wire[MADDR_WIDTH-1:0] addr4; assign addr4 = {addr_i[MADDR_WIDTH-1:2],2'b0}; always @(*) if (ce_i == 1'b0) begin inst_o = 32'h0; end else begin inst_o = {mem[addr4],mem[addr4+1],mem[addr4+2],mem[addr4+3]}; end//if function integer clog2 (input integer n); begin n = n - 1; for (clog2 = 0; n > 0; clog2 = clog2 + 1) n = n >> 1; end endfunction endmodule //rom.data 12 34 56 78 90 ab cd ef ``` > testbenchROM.v ```C= #include <stdio.h> #include <stdlib.h> #include <assert.h> #include "Vrom.h" #include "verilated_vcd_c.h" vluint64_t main_time = 0; double sc_time_stamp(int len) { return (main_time%len); } int main(int argc,char **argv) { Verilated::commandArgs(argc,argv); Verilated::traceEverOn(true); VerilatedVcdC* tfp = new VerilatedVcdC(); Vrom *top = new Vrom("top"); top->trace(tfp, 0); tfp->open("wave.vcd"); int pos=0; while(sc_time_stamp(20) < 19 && !Verilated::gotFinish()) { top->ce_i = 1; top->addr_i = pos; top->eval(); printf("pos = %d, data=%x\n",pos, top->inst_o); tfp->dump(main_time); //dump wave main_time += 1; pos++; } top->final(); tfp->close(); delete top; return 0; } ``` > make and run ```bash $ verilator -Wall --cc --exe --build testbenchROM.cpp rom.v --trace ``` ```bash $ make -C obj_dir -f Vrom.mk $ ./obj_dir/Vrom ``` ![](https://i.imgur.com/xoAXTN2.png) ![](https://i.imgur.com/Y2d8yEZ.png) # Home work > 加法/or/and 實作 1. 1bit ALU ![](https://i.imgur.com/egrR02S.png) 2. 利用1bit ALU 設計一個 4bits ALU ![](https://i.imgur.com/2i7LLiz.png) # reference > course source file t1 資料夾 > https://www.w3cschool.cn/verilog/verilog-rdio3o3b.html