Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bao’s Design Notebook

Week of September 14th

Team Meeting

  • September 15th: I attended the first team meeting of the semester on Zoom, which went through semester goals and possible group assignments.

Group B

Leads Support

  • After having my request to assist with the leads accepted by Hans, I assisted Hans and Paul with assessing the first onboarding lab for new members.

Week of September 21st

Team Meeting

  • September 22nd: I attended the first team meeting of the semester on Zoom, which went through RV32I.

Leads Support

  • I advised the leads to add numeric first arguments to $fatal calls and clarify always_comb’s execution at simulation start, combinational intent, and single-writer restrictions for Group A’s Onboarding Lab 2.

Week of September 28th

Team Meeting

  • September 29th: I attended the second team meeting of the semester on Zoom, which went through specifications of the modules briefly for purposes of aiding in the creation of the microarchitecture schema for Group B’s Week 3 tasks

Group B

  • I designed the JAL operand and control-path diagram, including J-type immediate generation and 32-bit sign extension.
  • I mapped connections to shared adders and multiplexers to calculate PC + immJ and write PC + 4 to rd.
  • I labeled bus widths and control signals, leaving final mux encodings and exception handling for shared integration

Leads Support

  • I advised the leads to print success only after all checks pass and then call $finish, add separate watchdog timeouts adjustable for longer tests, explicitly cast randomized data to 8 bits and control signals to 1 bit, and require positive integer WIDTH and power-of-two integer DEPTH >= 2 for Group A’s Onboarding Lab 3.

Week of October 5th

Team Meeting

  • October 6th: I attended the third team meeting of the semester on Zoom.

Week of October 12th

Team Meeting

  • October 13th: I attended the forth team meeting of the semester on Zoom

Core Modules

  • I registered my interest to work on top.sv (primary interest) and control_unit.sv (secondary interest) on the project repository.

Week of October 19th

Team Meeting

  • October 13th: I attended the fifth team meeting of the semester in person

Core Modules

  • I created a when2meet to find suitable time to work with Ray and Yuhan.
  • I started working on the top-level module’s testbench.

Week of October 26th

Core Modules

  • I implemented top.sv to connect all six core modules through the 32-bit PC, operand/writeback muxes, and branch/jump paths, with named memory and mux constants.
  • I added 16,384-halfword, little-endian instruction memory with configurable image loading, missing-file diagnostics, and alignment/range guards; invalid fetches return zero without trap handling.
  • I built top_tb.sv with four 10 ns clock cycles, VCD output, and SMOKE_CYCLES + 1 preloaded NOPs. It confirms simulation completion only, without assertions or randomization; four dependencies remain unfinished.
  • I enabled strict top lint and updated the Level 1 runner to reject failed commands, timeouts, and memory-image diagnostics. CI passed with Icarus 12 and Verilator 5.020: 3 passed, 0 failed, and 4 skipped.
src/top.sv
// single-cycle integration with a byte-addressed PC and halfword instruction memory
module top #(
    parameter INSTR_MEM_FILE = "assembler/out/instr.mem"
) (
    input logic clk
);
    // fixed power-of-two halfword capacity determines the address slices
    localparam int INSTR_MEM_HALFWORDS = 16384;
    localparam int INSTR_INDEX_BITS = $clog2(INSTR_MEM_HALFWORDS);

    // mux encodings match docs/control_table.csv
    localparam logic [1:0] ALUA_RS1 = 2'd0;
    localparam logic [1:0] ALUA_ZERO = 2'd1;
    localparam logic [1:0] ALUA_PC = 2'd2;
    localparam logic [1:0] WB_ALU = 2'd0;
    localparam logic [1:0] WB_LINK = 2'd1;
    localparam logic [1:0] WB_MEM = 2'd2;

    // instruction memory and address paths
    logic [15:0] instr_mem [0:INSTR_MEM_HALFWORDS-1];
    logic [INSTR_INDEX_BITS-1:0] instr_index;
    logic [31:0] instr;
    logic [31:0] pc;
    logic [31:0] pc_next;
    logic [31:0] pc_plus4;
    logic [31:0] pc_branch;

    // controls use the encodings in docs/control_table.csv
    logic branch_decoder_on;
    logic [2:0] funct3;
    logic mux_PC;
    logic [1:0] mux_reg;
    logic RegWrite;
    logic MemWrite;
    logic [1:0] mux_ALUA;
    logic mux_ALUB;
    logic [3:0] ALU_funct;
    logic sign_val;
    logic [1:0] read_size;

    // register, immediate, execution, and writeback paths
    logic [31:0] rs1_data;
    logic [31:0] rs2_data;
    logic [31:0] imm;
    logic [31:0] alu_a;
    logic [31:0] alu_b;
    logic [31:0] alu_out;
    logic zflag;
    logic branch_taken;
    logic [31:0] mem_data;
    logic [31:0] writeback_data;

`ifndef SYNTHESIS
    // simulation-only image handle
    integer image_file;
`endif

    // initialize PC before the first clock edge and load the image from the working directory
    // assembler images are padded; sparse fixtures must provide every fetched halfword
    initial begin
        pc = 32'b0;
        // an empty filename skips loading without clearing memory
        // external simulation initialization must finish before the first clock edge
        if (INSTR_MEM_FILE != "") begin
`ifndef SYNTHESIS
            image_file = $fopen(INSTR_MEM_FILE, "r");
            // stop simulation when the requested image cannot be opened
            if (image_file == 0) begin
                $fatal(1, "top: cannot open instruction image '%s'", INSTR_MEM_FILE);
            end
            $fclose(image_file);
`endif
            $readmemh(INSTR_MEM_FILE, instr_mem, 0, INSTR_MEM_HALFWORDS-1);
        end
    end

    // reject misaligned or out-of-range fetches before truncating the address
    // zero is an invalid-fetch marker, not a NOP or a reported architectural trap
    assign instr_index = pc[INSTR_INDEX_BITS:1]; // convert byte addresses to halfword indices
    // aligned instructions pair an even low halfword with the next odd halfword
    assign instr = (pc[31:INSTR_INDEX_BITS+1] == '0 && pc[1:0] == 2'b0)
                 ? {instr_mem[{instr_index[INSTR_INDEX_BITS-1:1], 1'b1}], instr_mem[instr_index]}
                 : 32'b0;

    // reuse PC plus four for sequential execution and link writeback
    assign pc_plus4 = pc + 32'd4;
    assign pc_branch = pc + imm;
    assign pc_next = mux_PC ? (branch_taken ? pc_branch : pc_plus4) : alu_out;

    // advance PC on the same rising edge as register and memory writes
    always @(posedge clk) begin
        pc <= pc_next;
    end

    // decode execution controls from the fetched instruction
    control_unit u_control_unit (
        .instr(instr),
        .branch_decoder_on(branch_decoder_on),
        .funct3(funct3),
        .mux_PC(mux_PC),
        .mux_reg(mux_reg),
        .RegWrite(RegWrite),
        .MemWrite(MemWrite),
        .mux_ALUA(mux_ALUA),
        .mux_ALUB(mux_ALUB),
        .ALU_funct(ALU_funct),
        .sign_val(sign_val),
        .read_size(read_size)
    );

    // read both source registers and write the selected result
    reg_file u_reg_file (
        .rs1(instr[19:15]),
        .rs2(instr[24:20]),
        .rd(instr[11:7]),
        .data_in(writeback_data),
        .RegWrite(RegWrite),
        .clk(clk),
        .rs1_out(rs1_data),
        .rs2_out(rs2_data)
    );

    // reconstruct the immediate without rescaling branch or jump offsets
    instr_imm u_instr_imm (
        .instr(instr),
        .imm(imm)
    );

    // select rs1, zero for LUI, or PC for AUIPC and JAL
    always_comb begin
        case (mux_ALUA)
            ALUA_RS1: alu_a = rs1_data;
            ALUA_ZERO: alu_a = 32'b0;
            ALUA_PC: alu_a = pc;
            default: alu_a = 32'b0;
        endcase
    end

    // select the second register or decoded immediate
    assign alu_b = mux_ALUB ? imm : rs2_data;

    // execute arithmetic and comparisons, including JALR low-bit clearing
    alu u_alu (
        .inA(alu_a),
        .inB(alu_b),
        .ALU_funct(ALU_funct),
        .out(alu_out),
        .zflag(zflag)
    );

    // combine the branch condition with ALU comparison results
    branch_decoder u_branch_decoder (
        .branch_decoder_on(branch_decoder_on),
        .funct3(funct3),
        .zflag(zflag),
        .ALU_out_lsb(alu_out[0]),
        .branch_decision(branch_taken)
    );

    // stores always use rs2, independently of the ALU operand muxes
    data_mem u_data_mem (
        .addr(alu_out),
        .data_in(rs2_data),
        .sign_val(sign_val),
        .read_size(read_size),
        .clk(clk),
        .MemWrite(MemWrite),
        .data_out(mem_data)
    );

    // select the ALU result, link address, or memory data
    always_comb begin
        case (mux_reg)
            WB_ALU: writeback_data = alu_out;
            WB_LINK: writeback_data = pc_plus4;
            WB_MEM: writeback_data = mem_data;
            default: writeback_data = 32'b0;
        endcase
    end
endmodule
testbench/level1_tb/top_tb.sv
// level 1 compilation and clocked simulation without functional output checks
module top_tb;
    // local timing keeps the clock independent of other modules
    timeunit 1ns;
    timeprecision 1ps;

    // one extra instruction covers the fetch after the final PC update
    localparam int SMOKE_CYCLES = 4;
    localparam int FIXTURE_INSTRUCTIONS = SMOKE_CYCLES + 1;
    localparam logic [31:0] NOP_INSTRUCTION = 32'h00000013;

    // clock stimulus and optional waveform path
    logic clk;
    string wave_file;

    // initialize the fixture below instead of loading an external image
    top #(
        .INSTR_MEM_FILE("")
    ) dut (
        .clk(clk)
    );

    // generate a waveform without replacing unfinished production modules
    initial begin
        clk = 1'b0;

        // preload little-endian NOPs before the first rising edge
        for (int index = 0; index < FIXTURE_INSTRUCTIONS; index++) begin
            dut.instr_mem[2 * index] = NOP_INSTRUCTION[15:0]; // low halfword first
            dut.instr_mem[2 * index + 1] = NOP_INSTRUCTION[31:16];
        end

        // accept the existing level 1 runner's waveform destination
        if (!$value$plusargs("wave=%s", wave_file)) begin
            wave_file = "top_tb.vcd";
        end
        $dumpfile(wave_file);
        $dumpvars(0, top_tb);
        $display("top: smoke simulation only; no functional output checks");
        $display("top: unfinished dependencies may propagate unknown values");

        // use the same cycle count for fixture sizing and clock stimulus
        repeat (SMOKE_CYCLES) begin
            #5 clk = 1'b1; // 10 ns clock period
            #5 clk = 1'b0;
        end

        // the runner marker reports simulation completion only
        $display("TEST PASSED: top compiled and completed %0d clock cycles", SMOKE_CYCLES);
        $finish;
    end
endmodule

Week of November 2nd

Microcontroller