diff --git a/dev/hdl/main.sv b/dev/hdl/main.sv index e9fafea..f537331 100644 --- a/dev/hdl/main.sv +++ b/dev/hdl/main.sv @@ -44,6 +44,13 @@ module main#( // External data bus. boa_mem_bus.CPU xmd_bus, + // Perform a release data fence. + output logic fence_rl, + // Perform an acquire data fence. + output logic fence_aq, + // Perform an acquire instruction fence. + output logic fence_i, + // Power management unit interface. pmu_bus.CPU pmb ); @@ -99,7 +106,7 @@ module main#( // CPU. logic[31:16] irq; - boa32_cpu#(32'h40001000, 32'hff000000, 0, 0) cpu(clk, rtc_clk, rst, pbus, dbus, irq); + boa32_cpu#(32'h40001000, 32'hff000000, 0, 0) cpu(clk, rtc_clk, rst, pbus, dbus, fence_rl, fence_aq, fence_i, irq); // Interrupts. assign irq[16] = tx_empty; diff --git a/fpga/cmod_a7/top.sv b/fpga/cmod_a7/top.sv index 55c0632..8968fff 100644 --- a/fpga/cmod_a7/top.sv +++ b/fpga/cmod_a7/top.sv @@ -61,6 +61,9 @@ module top( assign xmd_bus.ready = 1; assign xmd_bus.rdata = 0; + // Fence signals. + logic fence_rl, fence_aq, fence_i; + pmu_bus pmb(); main#( .rom_file({boa_parentdir(`__FILE__), "/../../prog/bootloader/build/rom.mem"}), @@ -72,6 +75,7 @@ module top( gpio_out, gpio_oe, gpio_in, randomness, xmp_bus, xmi_bus, xmd_bus, + fence_rl, fence_aq, fence_i, pmb ); diff --git a/hdl/boa32_cpu.sv b/hdl/boa32_cpu.sv index cea5f8a..530511e 100644 --- a/hdl/boa32_cpu.sv +++ b/hdl/boa32_cpu.sv @@ -65,6 +65,13 @@ module boa32_cpu#( // Data memory bus. boa_mem_bus.CPU dbus, + // Perform a release data fence. + output logic fence_rl, + // Perform an acquire data fence. + output logic fence_aq, + // Perform an acquire instruction fence. + output logic fence_i, + // External interrupts 16 to 31. input logic[31:16] irq ); @@ -230,6 +237,9 @@ module boa32_cpu#( /* ==== Data hazard avoidance ==== */ + // Is an instruction fetch fence instruction. + logic is_fencei; + // EX uses RS1 value. logic use_rs1_ex; // EX uses RS2 value. @@ -332,6 +342,7 @@ module boa32_cpu#( // Data dependency resolution. boa_stage_ex_fw st_ex_fw (id_ex_insn, use_rs1_ex, use_rs2_ex); boa_stage_mem_fw st_mem_fw(ex_mem_insn, use_rs1_mem, use_rs2_mem); + assign fence_i = is_fencei && !fw_stall_id; always @(*) begin fw_stall_mem = mem_stall_req; fw_stall_ex = ex_stall_req; @@ -366,6 +377,11 @@ module boa32_cpu#( // Stall ID so that MEM will produce a result that may then be used by the branch target address. fw_stall_id = 1; end + if (is_fencei && (ex_mem_valid || mem_wb_valid)) begin + // A fence.i instruction requires the rest of the pipeline to be emptied. + // Wait for the instructions in EX and MEM to either trap or finish. + fw_stall_id = 1; + end fw_stall_ex |= fw_stall_mem; fw_stall_id |= fw_stall_ex; @@ -476,6 +492,8 @@ module boa32_cpu#( clk, rst, clear_if, pbus, // Pipeline output. if_id_valid, if_id_pc, if_id_insn, if_id_trap, if_id_cause, + // Instruction fetch fence. + is_fencei, // Control transfer. fw_branch_predict, fw_branch_target, if_next_pc, fw_branch_correct, fw_branch_alt, fw_exception, fw_tvec, // Data hazard avoicance. @@ -487,6 +505,8 @@ module boa32_cpu#( if_id_valid && !fw_stall_if, if_id_pc, if_id_insn, if_id_trap && !fw_stall_if, if_id_cause, // Pipeline output. id_ex_valid, id_ex_pc, id_ex_insn, id_ex_ilen, id_ex_use_rd, id_ex_rs1_val, id_ex_rs2_val, id_ex_branch, id_ex_branch_predict, id_ex_trap, id_ex_cause, + // Instruction fetch fence. + is_fencei, // Control transfer. is_xret, is_sret, is_jump, is_branch, branch_predict, branch_target, // Write-back. @@ -505,6 +525,8 @@ module boa32_cpu#( ); boa_stage_mem st_mem( clk, rst, clear_mem, dbus_in, csr, + // Data fence. + fence_rl, fence_aq, // Pipeline input. ex_mem_valid && !fw_stall_ex, ex_mem_pc, ex_mem_insn, ex_mem_use_rd, fw_rs1_mem ? fw_in_rs1_mem : ex_mem_rs1_val, fw_rs2_mem ? fw_in_rs2_mem : ex_mem_rs2_val, ex_mem_trap && !fw_stall_ex, ex_mem_cause, // Pipeline output. diff --git a/hdl/stages/boa_stage_id.sv b/hdl/stages/boa_stage_id.sv index 5d51dd2..09cfd13 100644 --- a/hdl/stages/boa_stage_id.sv +++ b/hdl/stages/boa_stage_id.sv @@ -58,6 +58,9 @@ module boa_stage_id#( output logic[3:0] q_cause, + // FENCE.I instructions. + output logic is_fencei, + // MRET or SRET instruction. output logic is_xret, // Is SRET instead of MRET. @@ -155,6 +158,9 @@ module boa_stage_id#( wire is_ecall = insn[6:2] == `RV_OP_SYSTEM && insn[14:12] == 0 && insn[31:20] == 0; wire is_ebreak = insn[6:2] == `RV_OP_SYSTEM && insn[14:12] == 0 && insn[31:20] == 1; + // FENCE.I logic. + assign is_fencei = r_valid && r_insn[6:2] == `RV_OP_MISC_MEM && r_insn[1:0] == 3 && r_insn[14:12] == 1; + // Register decoder. logic use_rs1, use_rs2, use_rs3, use_rd; boa_reg_decoder reg_decd(insn, use_rs1, use_rs2, use_rs3, use_rd); @@ -515,7 +521,7 @@ module boa_insn_validator#( default: begin valid = 0; end `RV_OP_LOAD: begin valid = insn[14] ? (insn[13:12] < 2) + rv64 : (insn[13:12] < 3) + rv64; end `RV_OP_LOAD_FP: begin valid = 0; $strobe("TODO: validity for LOAD_FP"); end - `RV_OP_MISC_MEM: begin valid = insn[14:12] == 0; end + `RV_OP_MISC_MEM: begin valid = insn[14:12] == 0 || insn[14:12] == 1; end `RV_OP_OP_IMM: begin valid = valid_op_imm; end `RV_OP_AUIPC: begin valid = 1; end `RV_OP_OP_IMM_32: begin valid = rv64 && valid_op_imm; end diff --git a/hdl/stages/boa_stage_if.sv b/hdl/stages/boa_stage_if.sv index 30f9322..47cf288 100644 --- a/hdl/stages/boa_stage_if.sv +++ b/hdl/stages/boa_stage_if.sv @@ -34,6 +34,9 @@ module boa_stage_aligned_if#( output logic[3:0] q_cause, + // Instruction fetch fence. + input logic fence_i, + // Unconditional control transfer or branch predicted as taken. input logic fw_branch_predict, // Branch target address. @@ -88,7 +91,7 @@ module boa_stage_aligned_if#( assign q_trap = valid && q_pc[1]; assign q_cause = `RV_ECAUSE_IALIGN; - assign valid = pbus.ready && !fw_branch_predict && !fw_branch_correct && !clear; + assign valid = pbus.ready && !fw_branch_predict && !fw_branch_correct && !clear && !fence_i; assign q_pc = pc; assign q_insn = pbus.rdata; always @(posedge clk) begin @@ -131,6 +134,9 @@ module boa_stage_if#( output logic[3:0] q_cause, + // Instruction fetch fence. + input logic fence_i, + // Unconditional control transfer or branch predicted as taken. input logic fw_branch_predict, // Branch target address. @@ -201,7 +207,7 @@ module boa_stage_if#( generate for (x = 1; x < cache_depth; x = x + 1) begin always @(posedge clk) begin - if (rst) begin + if (rst || fence_i) begin icache[x] <= 'bx; acache[x] <= 'bx; cvalid[x] <= 0; @@ -237,7 +243,7 @@ module boa_stage_if#( assign pbus.we = 0; assign pbus.wdata = 'bx; always @(*) begin - if (rst) begin + if (rst || fence_i) begin // Reset; don't do anything. pbus.re = 0; pbus.addr = 'bx; @@ -257,7 +263,7 @@ module boa_stage_if#( end // Pipeline output logic. - assign q_valid = insn_valid && !clear; + assign q_valid = insn_valid && !clear && !fence_i; assign q_trap = 0; assign q_cause = 'bx; diff --git a/hdl/stages/boa_stage_mem.sv b/hdl/stages/boa_stage_mem.sv index 9efd4ff..90c96c0 100644 --- a/hdl/stages/boa_stage_mem.sv +++ b/hdl/stages/boa_stage_mem.sv @@ -20,6 +20,11 @@ module boa_stage_mem( // CSR access bus. boa_csr_bus.CPU csr, + // Perform a release data fence. + output logic fence_rl, + // Perform an acquire data fence. + output logic fence_aq, + // EX/MEM: Result valid. input logic d_valid, @@ -98,6 +103,12 @@ module boa_stage_mem( logic[3:0] cause; + /* ==== Fence logic ==== */ + // Is this a FENCE instruction? + logic is_fence = r_valid && !clear && !fw_stall_mem && r_insn[6:2] == `RV_OP_MISC_MEM && r_insn[14:12] == 0; + assign fence_aq = is_fence && r_insn[27:24] != 0; + assign fence_rl = is_fence && r_insn[23:20] != 0; + /* ==== Memory access logic ==== */ // Alignment error. logic ealign; diff --git a/prog/bootloader/src/main.c b/prog/bootloader/src/main.c index a040574..cf828bd 100644 --- a/prog/bootloader/src/main.c +++ b/prog/bootloader/src/main.c @@ -148,7 +148,8 @@ void p_speed() { } // Wait for UART to finish sending. - while (UART0.status.tx_busy || UART0.status.rx_hasdat); + while (UART0.status.tx_busy || UART0.status.rx_hasdat) + ; // Configure new frequency. UART0.clk_div = divider; } @@ -289,12 +290,16 @@ void isr() { // Does stuff? void main() { + asm("fence"); + asm("fence.i"); + // Blink the LED red at startup. if (!IS_SIMULATOR) { mtime = 0; GPIO.oe = 1 << 8; GPIO.port = 1 << 8; - while (mtime < 100000); + while (mtime < 100000) + ; GPIO.oe = 0; GPIO.port = 0; } diff --git a/prog/common/CMakeLists.txt b/prog/common/CMakeLists.txt index 9a8e0c9..f1e30ed 100644 --- a/prog/common/CMakeLists.txt +++ b/prog/common/CMakeLists.txt @@ -26,7 +26,7 @@ target_compile_definitions(${target} PUBLIC target_compile_options(${target} PUBLIC -ffreestanding -nostdlib -nodefaultlibs -nostdinc -O2 - -march=rv32imc_zicsr -mabi=ilp32 + -march=rv32imc_zicsr_zifencei -mabi=ilp32 -ffunction-sections ) diff --git a/sim/dev/hdl/top.sv b/sim/dev/hdl/top.sv index 48b4460..0be1155 100644 --- a/sim/dev/hdl/top.sv +++ b/sim/dev/hdl/top.sv @@ -28,6 +28,9 @@ module top( boa_mem_bus#(24) xmd_bus(); pmu_bus pmb(); + // Fence signals. + logic fence_rl, fence_aq, fence_i; + // Main microcontroller device. main#( .rom_file({boa_parentdir(`__FILE__), "/../obj_dir/rom.mem"}), @@ -40,6 +43,7 @@ module top( gpio_out, gpio_oe, gpio_in, randomness, xmp_bus, xmi_bus, xmd_bus, + fence_rl, fence_aq, fence_i, pmb ); diff --git a/sim/riscv-tests/hdl/top.sv b/sim/riscv-tests/hdl/top.sv index aa0b8b3..615ca29 100644 --- a/sim/riscv-tests/hdl/top.sv +++ b/sim/riscv-tests/hdl/top.sv @@ -50,12 +50,13 @@ module top( end // The boa CPU core. + logic fence_rl, fence_aq, fence_i; boa32_cpu#( .entrypoint(32'h8000_0000), .has_c(1), .has_m(1) ) cpu ( - clk, clk, rst, pbus, dbus, 0 + clk, clk, rst, pbus, dbus, fence_rl, fence_aq, fence_i, 0 ); endmodule diff --git a/sim/riscv-tests/tests.py b/sim/riscv-tests/tests.py index 9f9fa86..5e3cc9d 100755 --- a/sim/riscv-tests/tests.py +++ b/sim/riscv-tests/tests.py @@ -24,7 +24,7 @@ def list_tests(file): def compile_test(test): cc = os.environ.get("CC", "riscv32-unknown-elf-gcc") cp = os.environ.get("OBJCOPY", "riscv32-unknown-elf-objcopy") - isa = os.environ.get("ISA", "rv32imc_zicsr") + isa = os.environ.get("ISA", "rv32imc_zicsr_zifencei") abi = os.environ.get("ABI", "ilp32") Path("build/"+test).parent.mkdir(parents=True, exist_ok=True)