From 76f5b816bd9b4a6bb341f9c8457792a5d68ae979 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Wed, 4 Feb 2026 18:46:21 +0100 Subject: [PATCH 1/5] openroad: fix tie cell names --- openroad/scripts/init_tech.tcl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openroad/scripts/init_tech.tcl b/openroad/scripts/init_tech.tcl index 9244c59f..c7407cc6 100644 --- a/openroad/scripts/init_tech.tcl +++ b/openroad/scripts/init_tech.tcl @@ -72,8 +72,8 @@ proc setDefaultParasitics {} { } # Tie cell pins -set tieHiPin "TIEHI/Y" -set tieLoPin "TIELO/Y" +set tieHiPin "sg13g2_tiehi/L_HI" +set tieLoPin "sg13g2_tielo/L_LO" # Tap cell insertion proc insertTapCells {} { From 7ed72a9ba08d81b1a08e0a104c98305b1b3c2ea3 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Wed, 4 Feb 2026 12:15:41 +0100 Subject: [PATCH 2/5] hw: add wfi trampoline bootrom --- .github/scripts/check_sim.sh | 2 +- Bender.yml | 1 + rtl/bootrom/bootrom.sv | 81 +++++++++++++++++++++++++++++++++++ rtl/croc_domain.sv | 26 ++++++++--- rtl/croc_pkg.sv | 8 +++- rtl/soc_ctrl/soc_ctrl_regs.sv | 2 - rtl/test/tb_croc_pkg.sv | 3 ++ rtl/test/tb_croc_soc.sv | 9 +++- verilator/croc.f | 1 + vsim/compile_rtl.tcl | 1 + yosys/src/croc.flist | 1 + 11 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 rtl/bootrom/bootrom.sv diff --git a/.github/scripts/check_sim.sh b/.github/scripts/check_sim.sh index 707b4520..9973ec78 100755 --- a/.github/scripts/check_sim.sh +++ b/.github/scripts/check_sim.sh @@ -8,7 +8,7 @@ LOG_FILE=$1 expected_lines=( - "\[CORE\] Start fetching instructions" + "\[CORE\] Waking core via CLINT msip" "\[JTAG\] Halting hart 0" "\[JTAG\] Resumed hart 0" "\[UART\] Hello World!" diff --git a/Bender.yml b/Bender.yml index 8e0f4f5c..f4c5b4bc 100644 --- a/Bender.yml +++ b/Bender.yml @@ -39,6 +39,7 @@ sources: files: # Level 1 - rtl/core_wrap.sv + - rtl/bootrom/bootrom.sv - rtl/soc_ctrl/soc_ctrl_regs.sv - rtl/gpio/gpio_reg_top.sv - rtl/gpio/gpio.sv diff --git a/rtl/bootrom/bootrom.sv b/rtl/bootrom/bootrom.sv new file mode 100644 index 00000000..555cc0dd --- /dev/null +++ b/rtl/bootrom/bootrom.sv @@ -0,0 +1,81 @@ +// Copyright 2024 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Authors: +// - Philippe Sauter + +`include "common_cells/registers.svh" + +/// Bootrom containing a WFI trampoline. +/// After reset the core fetches from here, enables the M-mode interrupt +/// executes WFI, and waits until woken by CLINT msip. +/// On wake it clears msip, reads the boot address from soc_ctrl and jumps there. +module bootrom #( + /// The OBI configuration for all ports. + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + /// OBI request type + parameter type obi_req_t = logic, + /// OBI response type + parameter type obi_rsp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input obi_req_t obi_req_i, + output obi_rsp_t obi_rsp_o +); + + // Bootrom contents (9 words, 36 bytes) + // https://godbolt.org/z/MP5ejvzdf + localparam int unsigned RomSize = 9; + localparam logic [31:0] RomData [RomSize] = '{ + 32'h00800293, // addi t0, zero, 8 # t0 = 8 (MSIE bit) + 32'h30429073, // csrw mie, t0 # Enable M-mode software interrupt + 32'h10500073, // wfi # Sleep until interrupt + 32'h020402b7, // lui t0, 0x02040 # t0 = CLINT base (0x02040000) + 32'h0002a023, // sw zero, 0(t0) # Clear msip + 32'h30401073, // csrw mie, zero # Disable interrupts + 32'h030002b7, // lui t0, 0x03000 # t0 = SOC_CTRL base (0x03000000) + 32'h0002a283, // lw t0, 0(t0) # t0 = boot address + 32'h00028067 // jalr zero, 0(t0) # Jump to boot address + }; + + localparam int unsigned AddressWidth = cf_math_pkg::idx_width(RomSize) +2; // in bytes + + // Handle OBI requests + logic we_d, we_q; // delayed to the response phase + logic req_d, req_q; // delayed to the response phase + logic [ObiCfg.IdWidth-1:0] id_d, id_q; // delayed to the response phase + logic [AddressWidth-1:0] read_addr_d, read_addr_q; // delayed to the response phase (word addr) + + assign req_d = obi_req_i.req; + assign we_d = obi_req_i.a.we; + assign id_d = obi_req_i.a.aid; + assign read_addr_d = obi_req_i.a.addr[AddressWidth-1:2]; + + // Latch request for one-cycle response + `FF(req_q, req_d, '0, clk_i, rst_ni) + `FF(we_q, we_d, '0, clk_i, rst_ni) + `FF(id_q, id_d, '0, clk_i, rst_ni) + `FF(read_addr_q, read_addr_d, '0, clk_i, rst_ni) + + // Address range check + logic in_range; + assign in_range = (read_addr_q < RomSize); + + always_comb begin + obi_rsp_o = '0; + obi_rsp_o.gnt = 1'b1; // always grant + obi_rsp_o.rvalid = req_q; + obi_rsp_o.r.rid = id_q; + if (we_q || !in_range) begin + // write request or out of range + obi_rsp_o.r.rdata = 32'hBADCAB1E; + obi_rsp_o.r.err = 1'b1; + end else begin + obi_rsp_o.r.rdata = RomData[read_addr_q]; + obi_rsp_o.r.err = 1'b0; + end + end + +endmodule diff --git a/rtl/croc_domain.sv b/rtl/croc_domain.sv index db6f659b..2c0f38d6 100644 --- a/rtl/croc_domain.sv +++ b/rtl/croc_domain.sv @@ -50,7 +50,6 @@ module croc_domain import croc_pkg::*; #( logic sram_impl; // soc_ctrl -> SRAM config signals logic debug_req; logic fetch_enable; - logic [31:0] boot_addr; // interrupts (irqs) logic clint_timer_irq; @@ -162,6 +161,10 @@ module croc_domain import croc_pkg::*; #( // CLINT bus sbr_obi_req_t clint_obi_req; sbr_obi_rsp_t clint_obi_rsp; + + // Bootrom bus + sbr_obi_req_t bootrom_obi_req; + sbr_obi_rsp_t bootrom_obi_rsp; // Fanout to individual peripherals assign error_obi_req = all_periph_obi_req[PeriphError]; @@ -176,8 +179,10 @@ module croc_domain import croc_pkg::*; #( assign all_periph_obi_rsp[PeriphGpio] = gpio_obi_rsp; assign timer_obi_req = all_periph_obi_req[PeriphTimer]; assign all_periph_obi_rsp[PeriphTimer] = timer_obi_rsp; - assign clint_obi_req = all_periph_obi_req[PeriphClint]; - assign all_periph_obi_rsp[PeriphClint] = clint_obi_rsp; + assign clint_obi_req = all_periph_obi_req[PeriphClint]; + assign all_periph_obi_rsp[PeriphClint] = clint_obi_rsp; + assign bootrom_obi_req = all_periph_obi_req[PeriphBootrom]; + assign all_periph_obi_rsp[PeriphBootrom] = bootrom_obi_rsp; // ----------------- @@ -194,7 +199,7 @@ module croc_domain import croc_pkg::*; #( .timer_irq_i ( clint_timer_irq ), .software_irq_i ( clint_software_irq ), - .boot_addr_i ( boot_addr ), + .boot_addr_i ( BootromAddr ), .instr_req_o ( core_instr_obi_req.req ), .instr_gnt_i ( core_instr_obi_rsp.gnt ), @@ -477,7 +482,6 @@ module croc_domain import croc_pkg::*; #( .rst_ni, .obi_req_i ( soc_ctrl_obi_req ), .obi_rsp_o ( soc_ctrl_obi_rsp ), - .boot_addr_o ( boot_addr ), .fetch_en_o ( fetch_en_reg ), .sram_dly_o ( sram_impl ) ); @@ -555,6 +559,18 @@ module croc_domain import croc_pkg::*; #( .overflow_o ( ) // Not connected ); + // Bootrom + bootrom #( + .ObiCfg ( SbrObiCfg ), + .obi_req_t ( sbr_obi_req_t ), + .obi_rsp_t ( sbr_obi_rsp_t ) + ) i_bootrom ( + .clk_i, + .rst_ni, + .obi_req_i ( bootrom_obi_req ), + .obi_rsp_o ( bootrom_obi_rsp ) + ); + // Peripheral space error subordinate obi_err_sbr #( .ObiCfg ( SbrObiCfg ), diff --git a/rtl/croc_pkg.sv b/rtl/croc_pkg.sv index 31fd0a93..94b987a7 100644 --- a/rtl/croc_pkg.sv +++ b/rtl/croc_pkg.sv @@ -108,16 +108,18 @@ package croc_pkg; PeriphUart = 3, PeriphGpio = 4, PeriphTimer = 5, - PeriphClint = 6 + PeriphBootrom = 6, + PeriphClint = 7 } periph_outputs_e; /// Address map given to the peripheral mux - localparam addr_map_rule_t [5:0] periph_addr_map = '{ + localparam addr_map_rule_t [6:0] periph_addr_map = '{ '{ idx: PeriphDebug, start_addr: 32'h0000_0000, end_addr: 32'h0004_0000 }, '{ idx: PeriphSocCtrl, start_addr: 32'h0300_0000, end_addr: 32'h0300_1000 }, '{ idx: PeriphUart, start_addr: 32'h0300_2000, end_addr: 32'h0300_3000 }, '{ idx: PeriphGpio, start_addr: 32'h0300_5000, end_addr: 32'h0300_6000 }, '{ idx: PeriphTimer, start_addr: 32'h0300_A000, end_addr: 32'h0300_B000 }, + '{ idx: PeriphBootrom, start_addr: 32'h0200_0000, end_addr: 32'h0200_4000 }, '{ idx: PeriphClint, start_addr: 32'h0204_0000, end_addr: 32'h0208_0000 } }; @@ -137,6 +139,8 @@ package croc_pkg; return addr; endfunction + localparam bit [31:0] BootromAddr = get_periph_start_addr(PeriphBootrom); + /////////////////////////////////////////// // Interconnect Types and Configurations // diff --git a/rtl/soc_ctrl/soc_ctrl_regs.sv b/rtl/soc_ctrl/soc_ctrl_regs.sv index 14f34cf1..7cf8962d 100644 --- a/rtl/soc_ctrl/soc_ctrl_regs.sv +++ b/rtl/soc_ctrl/soc_ctrl_regs.sv @@ -15,7 +15,6 @@ module soc_ctrl_regs #( input obi_req_t obi_req_i, output obi_rsp_t obi_rsp_o, // To hardware - output logic [31:0] boot_addr_o, output logic fetch_en_o, output logic sram_dly_o ); @@ -44,7 +43,6 @@ module soc_ctrl_regs #( assign obi_req_d = obi_req_i; // Output assignment - assign boot_addr_o = boot_addr_q; assign fetch_en_o = fetch_en_q; assign sram_dly_o = sram_dly_q; diff --git a/rtl/test/tb_croc_pkg.sv b/rtl/test/tb_croc_pkg.sv index f6ed3894..322490dc 100644 --- a/rtl/test/tb_croc_pkg.sv +++ b/rtl/test/tb_croc_pkg.sv @@ -30,4 +30,7 @@ package tb_croc_pkg; localparam bit [31:0] FetchEnAddr = SocCtrlBaseAddr + soc_ctrl_regs_pkg::SOC_CTRL_FETCHEN_OFFSET; localparam bit [31:0] CoreStatusAddr = SocCtrlBaseAddr + soc_ctrl_regs_pkg::SOC_CTRL_CORESTATUS_OFFSET; + // CLINT base address (msip register is at offset 0) + localparam bit [31:0] ClintBaseAddr = croc_pkg::get_periph_start_addr(croc_pkg::PeriphClint); + endpackage diff --git a/rtl/test/tb_croc_soc.sv b/rtl/test/tb_croc_soc.sv index a3559254..1d75e3cf 100644 --- a/rtl/test/tb_croc_soc.sv +++ b/rtl/test/tb_croc_soc.sv @@ -134,12 +134,17 @@ module tb_croc_soc #( // write test value to sram i_vip.jtag_write_reg32(SramBaseAddr, 32'h1234_5678, 1'b1); - // load binary to sram - i_vip.jtag_load_hex(binary_path); $display("@%t | [CORE] Start fetching instructions", $time); fetch_en = 1'b1; + // load binary to sram + i_vip.jtag_load_hex(binary_path); + + // wake core from WFI by writing to CLINT msip + $display("@%t | [CORE] Waking core via CLINT msip", $time); + i_vip.jtag_write_reg32(ClintBaseAddr, 32'h1); + // halt core i_vip.jtag_halt(); diff --git a/verilator/croc.f b/verilator/croc.f index d9cafb41..a5a7fc30 100644 --- a/verilator/croc.f +++ b/verilator/croc.f @@ -156,6 +156,7 @@ ../rtl/clint/clint_reg_pkg.sv ../rtl/obi_timer/obi_timer_reg_pkg.sv ../rtl/core_wrap.sv +../rtl/bootrom/bootrom.sv ../rtl/soc_ctrl/soc_ctrl_regs.sv ../rtl/gpio/gpio_reg_top.sv ../rtl/gpio/gpio.sv diff --git a/vsim/compile_rtl.tcl b/vsim/compile_rtl.tcl index 47d560dd..016a9304 100644 --- a/vsim/compile_rtl.tcl +++ b/vsim/compile_rtl.tcl @@ -350,6 +350,7 @@ if {[catch { vlog -incr -sv \ "+incdir+$ROOT/rtl/common_cells/include" \ "+incdir+$ROOT/rtl/obi/include" \ "$ROOT/rtl/core_wrap.sv" \ + "$ROOT/rtl/bootrom/bootrom.sv" \ "$ROOT/rtl/soc_ctrl/soc_ctrl_regs.sv" \ "$ROOT/rtl/gpio/gpio_reg_top.sv" \ "$ROOT/rtl/gpio/gpio.sv" \ diff --git a/yosys/src/croc.flist b/yosys/src/croc.flist index 83c4727d..952a7d29 100644 --- a/yosys/src/croc.flist +++ b/yosys/src/croc.flist @@ -155,6 +155,7 @@ ../rtl/clint/clint_reg_pkg.sv ../rtl/obi_timer/obi_timer_reg_pkg.sv ../rtl/core_wrap.sv +../rtl/bootrom/bootrom.sv ../rtl/soc_ctrl/soc_ctrl_regs.sv ../rtl/gpio/gpio_reg_top.sv ../rtl/gpio/gpio.sv From 2313ad560d9f65c92b6f334787d63a0e028be634 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Wed, 4 Feb 2026 12:25:06 +0100 Subject: [PATCH 3/5] hw: replace fetch_en_i with testmode_i --- openroad/src/constraints.sdc | 10 +++++----- openroad/src/padring.tcl | 2 +- rtl/croc_chip.sv | 10 +++------- rtl/croc_domain.sv | 8 ++------ rtl/croc_soc.sv | 14 +------------- rtl/soc_ctrl/soc_ctrl_regs.sv | 2 +- rtl/test/croc_vip.sv | 3 +-- rtl/test/tb_croc_soc.sv | 8 -------- vsim/wave_rtl.do | 1 - vsim/wave_yosys.do | 1 - xilinx/hw/croc_xilinx.sv | 22 +++++++--------------- 11 files changed, 21 insertions(+), 60 deletions(-) diff --git a/openroad/src/constraints.sdc b/openroad/src/constraints.sdc index 47927f9f..4495e04e 100644 --- a/openroad/src/constraints.sdc +++ b/openroad/src/constraints.sdc @@ -83,9 +83,9 @@ set_max_delay 3.0 -from $JTAG_ASYNC_RSP_START -to $JTAG_ASYNC_RSP_END -ignore_cl puts "Input/Outputs..." # Reset should propagate to system domain within a clock cycle. -set_input_delay -max [ expr $TCK_JTG * 0.10 ] [get_ports rst_ni] -set_false_path -hold -from [get_ports rst_ni] -set_max_delay $TCK_SYS -from [get_ports rst_ni] +set_input_delay -max [ expr $TCK_JTG * 0.10 ] [get_ports {rst_ni testmode_i}] +set_false_path -hold -from [get_ports {rst_ni testmode_i}] +set_max_delay $TCK_SYS -from [get_ports {rst_ni testmode_i}] ########## @@ -109,8 +109,8 @@ set_max_delay $TCK_JTG -from [get_ports jtag_trst_ni] ########## puts "GPIO..." -set_input_delay -min -add_delay -clock clk_sys [ expr $TCK_SYS * 0.10 ] [get_ports {gpio* fetch_en_i}] -set_input_delay -max -add_delay -clock clk_sys [ expr $TCK_SYS * 0.30 ] [get_ports {gpio* fetch_en_i}] +set_input_delay -min -add_delay -clock clk_sys [ expr $TCK_SYS * 0.10 ] [get_ports {gpio*}] +set_input_delay -max -add_delay -clock clk_sys [ expr $TCK_SYS * 0.30 ] [get_ports {gpio*}] set_output_delay -min -add_delay -clock clk_sys [ expr $TCK_SYS * 0.10 ] [get_ports {gpio*}] set_output_delay -max -add_delay -clock clk_sys [ expr $TCK_SYS * 0.30 ] [get_ports {gpio*}] diff --git a/openroad/src/padring.tcl b/openroad/src/padring.tcl index 70dbdea9..1a7efc77 100644 --- a/openroad/src/padring.tcl +++ b/openroad/src/padring.tcl @@ -50,7 +50,7 @@ place_pad -row IO_WEST -location [expr {$westStart - 0*$westPitch}] "pad_vssio0 place_pad -row IO_WEST -location [expr {$westStart - 1*$westPitch}] "pad_vddio0" ; # pin no: 2 place_pad -row IO_WEST -location [expr {$westStart - 2*$westPitch}] "pad_uart_rx_i" ; # pin no: 3 place_pad -row IO_WEST -location [expr {$westStart - 3*$westPitch}] "pad_uart_tx_o" ; # pin no: 4 -place_pad -row IO_WEST -location [expr {$westStart - 4*$westPitch}] "pad_fetch_en_i" ; # pin no: 5 +place_pad -row IO_WEST -location [expr {$westStart - 4*$westPitch}] "pad_testmode_i" ; # pin no: 5 place_pad -row IO_WEST -location [expr {$westStart - 5*$westPitch}] "pad_status_o" ; # pin no: 6 place_pad -row IO_WEST -location [expr {$westStart - 6*$westPitch}] "pad_clk_i" ; # pin no: 7 place_pad -row IO_WEST -location [expr {$westStart - 7*$westPitch}] "pad_ref_clk_i" ; # pin no: 8 diff --git a/rtl/croc_chip.sv b/rtl/croc_chip.sv index 1efba1b9..275ecf7d 100644 --- a/rtl/croc_chip.sv +++ b/rtl/croc_chip.sv @@ -19,7 +19,7 @@ module croc_chip import croc_pkg::*; #() ( input wire uart_rx_i, output wire uart_tx_o, - input wire fetch_en_i, + input wire testmode_i, output wire status_o, inout wire gpio0_io, @@ -67,7 +67,7 @@ module croc_chip import croc_pkg::*; #() ( logic soc_clk_i; logic soc_rst_ni; logic soc_ref_clk_i; - logic soc_testmode; + logic soc_testmode_i; logic soc_jtag_tck_i; logic soc_jtag_trst_ni; @@ -75,7 +75,6 @@ module croc_chip import croc_pkg::*; #() ( logic soc_jtag_tdi_i; logic soc_jtag_tdo_o; - logic soc_fetch_en_i; logic soc_status_o; localparam int unsigned GpioCount = 32; @@ -87,8 +86,6 @@ module croc_chip import croc_pkg::*; #() ( sg13g2_IOPadIn pad_clk_i (.pad(clk_i), .p2c(soc_clk_i)); sg13g2_IOPadIn pad_rst_ni (.pad(rst_ni), .p2c(soc_rst_ni)); sg13g2_IOPadIn pad_ref_clk_i (.pad(ref_clk_i), .p2c(soc_ref_clk_i)); - assign soc_testmode_i = '0; - sg13g2_IOPadIn pad_jtag_tck_i (.pad(jtag_tck_i), .p2c(soc_jtag_tck_i)); sg13g2_IOPadIn pad_jtag_trst_ni (.pad(jtag_trst_ni), .p2c(soc_jtag_trst_ni)); sg13g2_IOPadIn pad_jtag_tms_i (.pad(jtag_tms_i), .p2c(soc_jtag_tms_i)); @@ -98,7 +95,7 @@ module croc_chip import croc_pkg::*; #() ( sg13g2_IOPadIn pad_uart_rx_i (.pad(uart_rx_i), .p2c(soc_uart_rx_i)); sg13g2_IOPadOut16mA pad_uart_tx_o (.pad(uart_tx_o), .c2p(soc_uart_tx_o)); - sg13g2_IOPadIn pad_fetch_en_i (.pad(fetch_en_i), .p2c(soc_fetch_en_i)); + sg13g2_IOPadIn pad_testmode_i (.pad(testmode_i), .p2c(soc_testmode_i)); sg13g2_IOPadOut16mA pad_status_o (.pad(status_o), .c2p(soc_status_o)); sg13g2_IOPadInOut30mA pad_gpio0_io (.pad(gpio0_io), .c2p(soc_gpio_o[0]), .p2c(soc_gpio_i[0]), .c2p_en(soc_gpio_out_en_o[0])); @@ -166,7 +163,6 @@ module croc_chip import croc_pkg::*; #() ( .rst_ni ( soc_rst_ni ), .ref_clk_i ( soc_ref_clk_i ), .testmode_i ( soc_testmode_i ), - .fetch_en_i ( soc_fetch_en_i ), .status_o ( soc_status_o ), .jtag_tck_i ( soc_jtag_tck_i ), diff --git a/rtl/croc_domain.sv b/rtl/croc_domain.sv index 2c0f38d6..38edf4ef 100644 --- a/rtl/croc_domain.sv +++ b/rtl/croc_domain.sv @@ -13,7 +13,6 @@ module croc_domain import croc_pkg::*; #( input logic rst_ni, input logic ref_clk_i, input logic testmode_i, - input logic fetch_en_i, input logic jtag_tck_i, input logic jtag_tdi_i, @@ -165,7 +164,7 @@ module croc_domain import croc_pkg::*; #( // Bootrom bus sbr_obi_req_t bootrom_obi_req; sbr_obi_rsp_t bootrom_obi_rsp; - + // Fanout to individual peripherals assign error_obi_req = all_periph_obi_req[PeriphError]; assign all_periph_obi_rsp[PeriphError] = error_obi_rsp; @@ -470,9 +469,6 @@ module croc_domain import croc_pkg::*; #( ); // SoC Control - logic fetch_en_reg; - assign fetch_enable = fetch_en_i | fetch_en_reg; - soc_ctrl_regs #( .obi_req_t ( sbr_obi_req_t ), .obi_rsp_t ( sbr_obi_rsp_t ), @@ -482,7 +478,7 @@ module croc_domain import croc_pkg::*; #( .rst_ni, .obi_req_i ( soc_ctrl_obi_req ), .obi_rsp_o ( soc_ctrl_obi_rsp ), - .fetch_en_o ( fetch_en_reg ), + .fetch_en_o ( fetch_enable ), .sram_dly_o ( sram_impl ) ); diff --git a/rtl/croc_soc.sv b/rtl/croc_soc.sv index 3caadd28..05c8887a 100644 --- a/rtl/croc_soc.sv +++ b/rtl/croc_soc.sv @@ -12,7 +12,6 @@ module croc_soc import croc_pkg::*; #( input logic rst_ni, input logic ref_clk_i, input logic testmode_i, - input logic fetch_en_i, output logic status_o, input logic jtag_tck_i, @@ -29,7 +28,7 @@ module croc_soc import croc_pkg::*; #( output logic [GpioCount-1:0] gpio_out_en_o // Output enable signal; 0 -> input, 1 -> output ); - logic synced_rst_n, synced_fetch_en; + logic synced_rst_n; rstgen i_rstgen ( .clk_i, @@ -39,16 +38,6 @@ module croc_soc import croc_pkg::*; #( .init_no ( ) ); - sync #( - .STAGES ( 2 ), - .ResetValue ( 1'b0 ) - ) i_ext_intr_sync ( - .clk_i, - .rst_ni ( synced_rst_n ), - .serial_i ( fetch_en_i ), - .serial_o ( synced_fetch_en ) - ); - // Connection between Croc_domain and User_domain: User Sbr, Croc Mgr sbr_obi_req_t user_sbr_obi_req; sbr_obi_rsp_t user_sbr_obi_rsp; @@ -69,7 +58,6 @@ croc_domain #( .rst_ni ( synced_rst_n ), .ref_clk_i, .testmode_i, - .fetch_en_i ( synced_fetch_en ), .jtag_tck_i, .jtag_tdi_i, diff --git a/rtl/soc_ctrl/soc_ctrl_regs.sv b/rtl/soc_ctrl/soc_ctrl_regs.sv index 7cf8962d..1736a2d9 100644 --- a/rtl/soc_ctrl/soc_ctrl_regs.sv +++ b/rtl/soc_ctrl/soc_ctrl_regs.sv @@ -118,7 +118,7 @@ module soc_ctrl_regs #( always_ff @(posedge clk_i or negedge rst_ni) begin if (~rst_ni) begin boot_addr_q <= BootAddrDefault; - fetch_en_q <= '0; + fetch_en_q <= '1; core_status_q <= '0; boot_mode_q <= '0; sram_dly_q <= '0; diff --git a/rtl/test/croc_vip.sv b/rtl/test/croc_vip.sv index 0dd38741..070b0be8 100644 --- a/rtl/test/croc_vip.sv +++ b/rtl/test/croc_vip.sv @@ -12,7 +12,6 @@ module croc_vip #( output logic rst_no, output logic sys_clk_o, output logic ref_clk_o, - input logic fetch_en_i, output logic jtag_tck_o, output logic jtag_trst_no, output logic jtag_tms_o, @@ -334,7 +333,7 @@ module croc_vip #( initial begin static byte_bt uart_read_buf[$]; byte_bt bite; - @(posedge fetch_en_i); + @(posedge rst_no); uart_read_buf.delete(); forever begin uart_read_byte(bite); diff --git a/rtl/test/tb_croc_soc.sv b/rtl/test/tb_croc_soc.sv index 1d75e3cf..9a55aef1 100644 --- a/rtl/test/tb_croc_soc.sv +++ b/rtl/test/tb_croc_soc.sv @@ -35,7 +35,6 @@ module tb_croc_soc #( logic [GpioCount-1:0] gpio_out_en; // Signals controlled by the testbench - logic fetch_en; ///////////////////////////// // Command Line Arguments // @@ -73,7 +72,6 @@ module tb_croc_soc #( .rst_no ( rst_n ), .sys_clk_o ( sys_clk ), .ref_clk_o ( ref_clk ), - .fetch_en_i ( fetch_en ), .jtag_tck_o ( jtag_tck ), .jtag_trst_no ( jtag_trst_n ), .jtag_tms_o ( jtag_tms ), @@ -101,7 +99,6 @@ module tb_croc_soc #( .rst_ni ( rst_n ), .ref_clk_i ( ref_clk ), .testmode_i ( 1'b0 ), - .fetch_en_i ( fetch_en ), .status_o ( ), .jtag_tck_i ( jtag_tck ), .jtag_tdi_i ( jtag_tdi ), @@ -124,8 +121,6 @@ module tb_croc_soc #( initial begin $timeformat(-9, 0, "ns", 12); // 1: scale (ns=-9), 2: decimals, 3: suffix, 4: print-field width - fetch_en = 1'b0; - // wait for reset #ClkPeriodSys; @@ -135,9 +130,6 @@ module tb_croc_soc #( // write test value to sram i_vip.jtag_write_reg32(SramBaseAddr, 32'h1234_5678, 1'b1); - $display("@%t | [CORE] Start fetching instructions", $time); - fetch_en = 1'b1; - // load binary to sram i_vip.jtag_load_hex(binary_path); diff --git a/vsim/wave_rtl.do b/vsim/wave_rtl.do index b82b35d3..1c010851 100644 --- a/vsim/wave_rtl.do +++ b/vsim/wave_rtl.do @@ -15,7 +15,6 @@ add wave -noupdate -expand -group {Reset & Clocks} -label rst_ni /tb_croc_soc/i add wave -noupdate -expand -group {Reset & Clocks} -label clk_i /tb_croc_soc/i_croc_soc/clk_i add wave -noupdate -expand -group {Reset & Clocks} -label ref_clk_i /tb_croc_soc/i_croc_soc/ref_clk_i add wave -noupdate -expand -group {Mode & Status} -label testmode_i /tb_croc_soc/i_croc_soc/testmode_i -add wave -noupdate -expand -group {Mode & Status} -label fetch_en_i /tb_croc_soc/i_croc_soc/fetch_en_i add wave -noupdate -expand -group {Mode & Status} -label status_o /tb_croc_soc/i_croc_soc/status_o add wave -noupdate -expand -group JTAG -label jtag_tck_i /tb_croc_soc/i_croc_soc/jtag_tck_i add wave -noupdate -expand -group JTAG -label jtag_tdi_i /tb_croc_soc/i_croc_soc/jtag_tdi_i diff --git a/vsim/wave_yosys.do b/vsim/wave_yosys.do index 0b6a7f36..ffb24365 100644 --- a/vsim/wave_yosys.do +++ b/vsim/wave_yosys.do @@ -27,7 +27,6 @@ add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/data_req_o add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/data_rvalid_i add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/data_wdata_o add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/data_we_o -add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/fetch_enable_i add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/instr_addr_o add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/instr_gnt_i add wave -noupdate /tb_croc_soc/i_croc_soc/i_croc/i_core_wrap/instr_rdata_i diff --git a/xilinx/hw/croc_xilinx.sv b/xilinx/hw/croc_xilinx.sv index a9c5485c..a2045098 100644 --- a/xilinx/hw/croc_xilinx.sv +++ b/xilinx/hw/croc_xilinx.sv @@ -37,7 +37,6 @@ module croc_xilinx import croc_pkg::*; #( `endif `ifdef USE_SWITCHES - input logic fetch_en_i, // switch 7 input logic [GpioCount-1:0] gpio_i, // switch 0-3 `endif @@ -107,10 +106,8 @@ module croc_xilinx import croc_pkg::*; #( // Tie off inputs of no switches `ifndef USE_SWITCHES - logic fetch_en_i; logic [GpioCount-2:0] gpio_i; assign test_mode_i = '0; - assign fetch_en_i = '0; assign gpio_i = '0; `endif @@ -125,19 +122,17 @@ module croc_xilinx import croc_pkg::*; #( //////////// // VIOs // //////////// - logic vio_reset, vio_fetch_en, vio_gpio; + logic vio_reset, vio_gpio; `ifdef USE_VIO vio i_vio ( - .clk ( soc_clk ), - .probe_out0 ( vio_reset ), - .probe_out1 ( vio_fetch_en ), - .probe_out2 ( vio_gpio ) + .clk ( soc_clk ), + .probe_out0 ( vio_reset ), + .probe_out1 ( vio_gpio ) ); `else - assign vio_reset = '0; - assign vio_fetch_en = '0; - assign vio_gpio = '0; + assign vio_reset = '0; + assign vio_gpio = '0; `endif @@ -145,11 +140,9 @@ module croc_xilinx import croc_pkg::*; #( // SOC IO // ////////////// - logic soc_fetch_en; logic soc_rst_n; - assign soc_fetch_en = fetch_en_i | vio_fetch_en; - assign soc_rst = ~sys_resetn | vio_reset; + assign soc_rst = ~sys_resetn | vio_reset; logic [GpioCount-1:0] soc_gpio_i; logic [GpioCount-1:0] soc_gpio_o; @@ -251,7 +244,6 @@ module croc_xilinx import croc_pkg::*; #( .rst_ni ( rst_n ), .ref_clk_i ( rtc_clk_q ), .testmode_i ( soc_testmode_i ), - .fetch_en_i ( soc_fetch_en ), .status_o ( status_o ), .jtag_tck_i ( jtag_tck_i ), From e98da67c286047ae7846759f94cbb3fa08cf5998 Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Wed, 4 Feb 2026 18:32:23 +0100 Subject: [PATCH 4/5] sw: move crt0.S functions to bootrom --- rtl/bootrom/bootrom.S | 155 +++++++++++++++++++++++++++++++++ rtl/bootrom/bootrom.sv | 188 ++++++++++++++++++++++++++++++++++------- sw/crt0.S | 149 +++++++++----------------------- sw/link.ld | 13 ++- 4 files changed, 365 insertions(+), 140 deletions(-) create mode 100644 rtl/bootrom/bootrom.S diff --git a/rtl/bootrom/bootrom.S b/rtl/bootrom/bootrom.S new file mode 100644 index 00000000..51311593 --- /dev/null +++ b/rtl/bootrom/bootrom.S @@ -0,0 +1,155 @@ +# Copyright (c) 2026 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Authors: +# - Philippe Sauter +# +# Bootrom for the Croc SoC +# Located at 0x0200_0000 +# +# Boot flow: +# 1. Enable MSIE, WFI (wait until woken by JTAG via msip) +# 2. Wake: clear msip, disable interrupts +# 3. Clear registers x1, x3-x15 (x2/SP set by SRAM stub) +# 4. Set mtvec to bootrom trap handler +# 5. Read boot address from soc_ctrl, jump there with jalr zero +# (ra is set to &_eoc so main() returning goes to _eoc) +# 6. _eoc: pack return value, write CORESTATUS, WFI +# +# Trap handling: +# The bootrom trap handler saves/restores caller-saved registers and dispatches +# to user handlers via a function pointer vector table at fixed addresses: +# - 0x1000_0000: j _start (skip table) +# - 0x1000_0004: .word croc_exception_handler (function pointer) +# - 0x1000_0008: .word croc_interrupt_handler (function pointer) +# +# Fixed section offsets (from base 0x0200_0000): +# 0x000 _start (WFI trampoline + boot) +# 0x100 _eoc (end of computation) +# 0x200 _trap_handler_wrapper (trap entry, save regs, dispatch) +# 0x300 _trap_exit (restore regs, mret) + +# ----------------------------------------------------------------------------- +# WFI Trampoline (entry point after reset) +# ----------------------------------------------------------------------------- +.section .text +.globl _start +_start: + addi t0, zero, 8 # t0 = 8 (MSIE bit, bit 3) + csrw mie, t0 # Enable M-mode software interrupt + wfi # Sleep until interrupt + lui t0, 0x02040 # t0 = CLINT base (0x0204_0000) + sw zero, 0(t0) # Clear msip + csrw mie, zero # Disable all interrupts + +# ----------------------------------------------------------------------------- +# Register Initialization +# Clear x1, x3-x15. Skip x2 stack pointer, set by main sw (needs SRAM size) +# ----------------------------------------------------------------------------- + addi x1, zero, 0 + addi x3, zero, 0 + addi x4, zero, 0 + addi x5, zero, 0 + addi x6, zero, 0 + addi x7, zero, 0 + addi x8, zero, 0 + addi x9, zero, 0 + addi x10, zero, 0 + addi x11, zero, 0 + addi x12, zero, 0 + addi x13, zero, 0 + addi x14, zero, 0 + addi x15, zero, 0 + +# ----------------------------------------------------------------------------- +# CSR Initialization +# Set mtvec to bootrom trap handler +# ----------------------------------------------------------------------------- + la t0, _trap_handler_wrapper + csrw mtvec, t0 + +# ----------------------------------------------------------------------------- +# Boot software +# Read address from soc_ctrl register and jump +# ----------------------------------------------------------------------------- + lui t0, 0x03000 # t0 = SOC_CTRL base (0x0300_0000) + lw t0, 0(t0) # t0 = boot address (BOOTADDR register) + la ra, _eoc # ra = &_eoc (return address for main) + jalr zero, 0(t0) # Jump to boot address (rd=zero, ra preserved) + +# ----------------------------------------------------------------------------- +# End of Computation (_eoc) +# Reached when main() returns (via ra set above) +# Pack return value and write to CORESTATUS register +# ----------------------------------------------------------------------------- +.org 0x100 +.globl _eoc +_eoc: + slli t0, a0, 1 # Shift return code left by 1 + ori t0, t0, 1 # Set bit 0 (EOC flag) + lui t1, 0x03000 # t1 = SOC_CTRL base (0x0300_0000) + sw t0, 8(t1) # Write to CORESTATUS (offset 0x08) + wfi # Halt + +# ----------------------------------------------------------------------------- +# Trap Handler Wrapper +# Saves caller-saved registers, reads mcause, +# dispatches to interrupt/exception handler in SRAM via function pointer table +# ----------------------------------------------------------------------------- +.org 0x200 +_trap_handler_wrapper: + addi sp, sp, -80 + sw ra, 72(sp) + sw t0, 64(sp) + sw t1, 56(sp) + sw t2, 48(sp) + sw a0, 40(sp) + sw a1, 32(sp) + sw a2, 24(sp) + sw a3, 16(sp) + sw a4, 8(sp) + sw a5, 0(sp) + + # Read trap cause + # mcause[31] == 1 -> Interrupt + # mcause[31] == 0 -> Exception + csrr a0, mcause + blt a0, zero, _handle_interrupt + + # Exception: call handler via SRAM vector table + lui t0, 0x10000 # t0 = SRAM base (0x1000_0000) + lw t0, 4(t0) # t0 = *(0x1000_0004) = exception handler + jalr ra, 0(t0) # Call exception handler + j _trap_exit + +# ----------------------------------------------------------------------------- +# Interrupt Handler +# Strips mcause MSB to get interrupt ID, dispatches to user handler in SRAM +# ----------------------------------------------------------------------------- +_handle_interrupt: + slli a0, a0, 1 # Strip MSB (shift left) + srli a0, a0, 1 # Restore (shift right) -> a0 = interrupt ID + lui t0, 0x10000 # t0 = SRAM base (0x1000_0000) + lw t0, 8(t0) # t0 = *(0x1000_0008) = interrupt handler + jalr ra, 0(t0) # Call interrupt handler + j _trap_exit + +# ----------------------------------------------------------------------------- +# Trap Exit +# Restore caller-saved registers and return from trap +# ----------------------------------------------------------------------------- +.org 0x300 +_trap_exit: + lw ra, 72(sp) + lw t0, 64(sp) + lw t1, 56(sp) + lw t2, 48(sp) + lw a0, 40(sp) + lw a1, 32(sp) + lw a2, 24(sp) + lw a3, 16(sp) + lw a4, 8(sp) + lw a5, 0(sp) + addi sp, sp, 80 + mret diff --git a/rtl/bootrom/bootrom.sv b/rtl/bootrom/bootrom.sv index 555cc0dd..b1552234 100644 --- a/rtl/bootrom/bootrom.sv +++ b/rtl/bootrom/bootrom.sv @@ -1,4 +1,4 @@ -// Copyright 2024 ETH Zurich and University of Bologna. +// Copyright 2026 ETH Zurich and University of Bologna. // Solderpad Hardware License, Version 0.51, see LICENSE for details. // SPDX-License-Identifier: SHL-0.51 // @@ -7,10 +7,15 @@ `include "common_cells/registers.svh" -/// Bootrom containing a WFI trampoline. -/// After reset the core fetches from here, enables the M-mode interrupt -/// executes WFI, and waits until woken by CLINT msip. -/// On wake it clears msip, reads the boot address from soc_ctrl and jumps there. +/// Bootrom containing WFI trampoline, register init, trap handlers, and EOC. +/// After reset the core fetches from here: +/// 1. Enables MSIE, executes WFI, waits until woken by CLINT msip +/// 2. Clears msip, disables interrupts, clears registers x1/x3-x15 +/// 3. Sets mtvec to bootrom trap handler +/// 4. Reads boot address from soc_ctrl, jumps there with jalr zero (ra = &_eoc) +/// 5. On main() return: _eoc packs retval, writes CORESTATUS, halts +/// Trap handler dispatches to SRAM function pointer table at 0x1000_0000. +/// Source: bootrom.S (regenerate with: make -C rtl/bootrom) module bootrom #( /// The OBI configuration for all ports. parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, @@ -25,55 +30,176 @@ module bootrom #( output obi_rsp_t obi_rsp_o ); - // Bootrom contents (9 words, 36 bytes) - // https://godbolt.org/z/MP5ejvzdf - localparam int unsigned RomSize = 9; - localparam logic [31:0] RomData [RomSize] = '{ - 32'h00800293, // addi t0, zero, 8 # t0 = 8 (MSIE bit) - 32'h30429073, // csrw mie, t0 # Enable M-mode software interrupt - 32'h10500073, // wfi # Sleep until interrupt - 32'h020402b7, // lui t0, 0x02040 # t0 = CLINT base (0x02040000) - 32'h0002a023, // sw zero, 0(t0) # Clear msip - 32'h30401073, // csrw mie, zero # Disable interrupts - 32'h030002b7, // lui t0, 0x03000 # t0 = SOC_CTRL base (0x03000000) - 32'h0002a283, // lw t0, 0(t0) # t0 = boot address - 32'h00028067 // jalr zero, 0(t0) # Jump to boot address + //--------------------------------------------------------------------------------------------- + // Bootrom devided into blocks per contigous label + //--------------------------------------------------------------------------------------------- + // Contiguous block starting at 0x02000000: _start + localparam int unsigned StartRomWords = 28; + localparam logic [31:0] StartRom [StartRomWords] = '{ + // <_start> + 32'h00800293, // 0x02000000: addi x5,x0,8 + 32'h30429073, // 0x02000004: csrrw x0,mie,x5 + 32'h10500073, // 0x02000008: wfi + 32'h020402B7, // 0x0200000C: lui x5,0x2040 + 32'h0002A023, // 0x02000010: sw x0,0(x5) # 2040000 <__global_pointer$+0x3e4d0> + 32'h30401073, // 0x02000014: csrrw x0,mie,x0 + 32'h00000093, // 0x02000018: addi x1,x0,0 + 32'h00000193, // 0x0200001C: addi x3,x0,0 + 32'h00000213, // 0x02000020: addi x4,x0,0 + 32'h00000293, // 0x02000024: addi x5,x0,0 + 32'h00000313, // 0x02000028: addi x6,x0,0 + 32'h00000393, // 0x0200002C: addi x7,x0,0 + 32'h00000413, // 0x02000030: addi x8,x0,0 + 32'h00000493, // 0x02000034: addi x9,x0,0 + 32'h00000513, // 0x02000038: addi x10,x0,0 + 32'h00000593, // 0x0200003C: addi x11,x0,0 + 32'h00000613, // 0x02000040: addi x12,x0,0 + 32'h00000693, // 0x02000044: addi x13,x0,0 + 32'h00000713, // 0x02000048: addi x14,x0,0 + 32'h00000793, // 0x0200004C: addi x15,x0,0 + 32'h00000297, // 0x02000050: auipc x5,0x0 + 32'h1B028293, // 0x02000054: addi x5,x5,432 # 2000200 <_trap_handler_wrapper> + 32'h30529073, // 0x02000058: csrrw x0,mtvec,x5 + 32'h030002B7, // 0x0200005C: lui x5,0x3000 + 32'h0002A283, // 0x02000060: lw x5,0(x5) # 3000000 <__global_pointer$+0xffe4d0> + 32'h00000097, // 0x02000064: auipc x1,0x0 + 32'h09C08093, // 0x02000068: addi x1,x1,156 # 2000100 <_eoc> + 32'h00028067 // 0x0200006C: jalr x0,0(x5) }; - localparam int unsigned AddressWidth = cf_math_pkg::idx_width(RomSize) +2; // in bytes + // Contiguous block starting at 0x02000100: _eoc + localparam int unsigned EocRomWords = 5; + localparam logic [31:0] EocRomData [EocRomWords] = '{ + // <_eoc> + 32'h00151293, // 0x02000100: slli x5,x10,0x1x00010413 + 32'h0012E293, // 0x02000104: ori x5,x5,1 + 32'h03000337, // 0x02000108: lui x6,0x3000 + 32'h00532423, // 0x0200010C: sw x5,8(x6) # 3000008 <__global_pointer$+0xffe4d8> + 32'h10500073 // 0x02000110: wfi + }; + + // Contiguous block starting at 0x02000200: _trap_handler_wrapper + localparam int unsigned TrapHandlerRomWords = 23; + localparam logic [31:0] TrapHandlerRomData [TrapHandlerRomWords] = '{ + // <_trap_handler_wrapper> + 32'hFB010113, // 0x02000200: addi x2,x2,-80 + 32'h04112423, // 0x02000204: sw x1,72(x2) + 32'h04512023, // 0x02000208: sw x5,64(x2) + 32'h02612C23, // 0x0200020C: sw x6,56(x2) + 32'h02712823, // 0x02000210: sw x7,48(x2) + 32'h02A12423, // 0x02000214: sw x10,40(x2) + 32'h02B12023, // 0x02000218: sw x11,32(x2) + 32'h00C12C23, // 0x0200021C: sw x12,24(x2) + 32'h00D12823, // 0x02000220: sw x13,16(x2) + 32'h00E12423, // 0x02000224: sw x14,8(x2) + 32'h00F12023, // 0x02000228: sw x15,0(x2) + 32'h34202573, // 0x0200022C: csrrs x10,mcause,x0 + 32'h00054A63, // 0x02000230: blt x10,x0,2000244 <_handle_interrupt> + 32'h100002B7, // 0x02000234: lui x5,0x10000 + 32'h0042A283, // 0x02000238: lw x5,4(x5) # 10000004 <__global_pointer$+0xdffe4d4> + 32'h000280E7, // 0x0200023C: jalr x1,0(x5) + 32'h0C00006F, // 0x02000240: jal x0,2000300 <_trap_exit> + // <_handle_interrupt> + 32'h00151513, // 0x02000244: slli x10,x10,0x1 + 32'h00155513, // 0x02000248: srli x10,x10,0x1 + 32'h100002B7, // 0x0200024C: lui x5,0x10000 + 32'h0082A283, // 0x02000250: lw x5,8(x5) # 10000008 <__global_pointer$+0xdffe4d8> + 32'h000280E7, // 0x02000254: jalr x1,0(x5) + 32'h0A80006F // 0x02000258: jal x0,2000300 <_trap_exit> + }; + // Contiguous block starting at 0x02000300: _trap_exit + localparam int unsigned TrapExitRomWords = 12; + localparam logic [31:0] TrapExitRomData [TrapExitRomWords] = '{ + // <_trap_exit> + 32'h04812083, // 0x02000300: lw x1,72(x2) + 32'h04012283, // 0x02000304: lw x5,64(x2) + 32'h03812303, // 0x02000308: lw x6,56(x2) + 32'h03012383, // 0x0200030C: lw x7,48(x2) + 32'h02812503, // 0x02000310: lw x10,40(x2) + 32'h02012583, // 0x02000314: lw x11,32(x2) + 32'h01812603, // 0x02000318: lw x12,24(x2) + 32'h01012683, // 0x0200031C: lw x13,16(x2) + 32'h00812703, // 0x02000320: lw x14,8(x2) + 32'h00012783, // 0x02000324: lw x15,0(x2) + 32'h05010113, // 0x02000328: addi x2,x2,80 + 32'h30200073 // 0x0200032C: mret + }; + + + + // -------------------------------------------------------------------------- // Handle OBI requests - logic we_d, we_q; // delayed to the response phase - logic req_d, req_q; // delayed to the response phase - logic [ObiCfg.IdWidth-1:0] id_d, id_q; // delayed to the response phase - logic [AddressWidth-1:0] read_addr_d, read_addr_q; // delayed to the response phase (word addr) + // -------------------------------------------------------------------------- + localparam int unsigned WordAddressWidth = 10; // 12bit byte address + + logic we_d, we_q; + logic req_d, req_q; + logic [ObiCfg.IdWidth-1:0] id_d, id_q; + logic [WordAddressWidth-1:0] word_addr_d, word_addr_q; assign req_d = obi_req_i.req; assign we_d = obi_req_i.a.we; assign id_d = obi_req_i.a.aid; - assign read_addr_d = obi_req_i.a.addr[AddressWidth-1:2]; + assign word_addr_d = obi_req_i.a.addr[WordAddressWidth+2-1:2]; // Latch request for one-cycle response `FF(req_q, req_d, '0, clk_i, rst_ni) `FF(we_q, we_d, '0, clk_i, rst_ni) `FF(id_q, id_d, '0, clk_i, rst_ni) - `FF(read_addr_q, read_addr_d, '0, clk_i, rst_ni) + `FF(word_addr_q, word_addr_d, '0, clk_i, rst_ni) - // Address range check - logic in_range; - assign in_range = (read_addr_q < RomSize); + // -------------------------------------------------------------------------- + // Mask-based ROM decode: + // - upper 4 bits of the *word address* select the ROM + // - lower 6 bits select the word within that block + // -------------------------------------------------------------------------- + logic rom_req; + logic [ 3:0] rom_select; + logic [ 5:0] rom_idx, rom_size; + logic rom_error; + logic [31:0] rom_rdata; + + assign rom_req = req_q && !we_d; + assign rom_select = word_addr_q[WordAddressWidth-1 -: 4]; + assign rom_idx = word_addr_q[5:0]; + + always_comb begin + rom_rdata = 32'h0000_0000; + rom_error = 1'b1; + + case (rom_select) + 4'b0000: rom_size = StartRomWords; + 4'b0001: rom_size = EocRomWords; + 4'b0010: rom_size = TrapHandlerRomWords; + 4'b0011: rom_size = TrapExitRomWords; + default: rom_size = 8'h00; + endcase + + rom_error = (rom_idx >= rom_size); + + if (!rom_error && rom_req) begin + case (rom_select) + 4'b0000: rom_rdata = StartRom[rom_idx]; + 4'b0001: rom_rdata = EocRomData[rom_idx]; + 4'b0010: rom_rdata = TrapHandlerRomData[rom_idx]; + 4'b0011: rom_rdata = TrapExitRomData[rom_idx]; + default: rom_rdata = 32'h0000_0000; + endcase + end + end always_comb begin obi_rsp_o = '0; obi_rsp_o.gnt = 1'b1; // always grant obi_rsp_o.rvalid = req_q; obi_rsp_o.r.rid = id_q; - if (we_q || !in_range) begin - // write request or out of range + if (we_q) begin + // write request obi_rsp_o.r.rdata = 32'hBADCAB1E; obi_rsp_o.r.err = 1'b1; end else begin - obi_rsp_o.r.rdata = RomData[read_addr_q]; + obi_rsp_o.r.rdata = rom_rdata; obi_rsp_o.r.err = 1'b0; end end diff --git a/sw/crt0.S b/sw/crt0.S index 67ab9d58..829468f7 100644 --- a/sw/crt0.S +++ b/sw/crt0.S @@ -5,126 +5,59 @@ # Authors: # - Paul Scheffler # - Philippe Sauter +# +# Minimal SRAM startup stub. +# Register init, trap handling, and EOC are in the bootrom. +# This file provides: +# - Vector table at 0x1000_0000 (j _start + function pointers for +# exception/interrupt handlers read by the bootrom trap handler) +# - _start: set SP and GP, then tail-call main +# - Weak default handlers (just ret) + +# ================================================================== +# Vector table at SRAM base address (read by bootrom trap handler) +# ================================================================== +.section .vectors, "ax", @progbits + .option push + .option norvc # forbid compressed encodings in this section + .align 2 # 4-byte align + jal x0, _start # Skip over function pointer table + .word croc_exception_handler # Pointer read by bootrom at offset 4 + .word croc_interrupt_handler # Pointer read by bootrom at offset 8 + .option pop + +# ================================================================== +# Startup: set stack/global pointer, then jump to main +# ================================================================== .globl _start .section .text._start _start: - # Global pointer + la sp, __stack_pointer$ .option push .option norelax - la x3, __global_pointer$ + la gp, __global_pointer$ .option pop - # Stack pointer - la x2, __stack_pointer$ - # Reset vector - li x1, 0 - li x4, 0 - li x5, 0 - li x6, 0 - li x7, 0 - li x8, 0 - li x9, 0 - li x10, 0 - li x11, 0 - li x12, 0 - li x13, 0 - li x14, 0 - li x15, 0 - # Set trap handler wrapper - la t0, _trap_handler_wrapper - csrw mtvec, t0 - call main -_eoc: - # Write status register in SoC control regs - # Bit 0 signals end of computation - # Bits 31:1 contain return code - slli t0, a0, 1 - ori t0, t0, 1 - la t1, status - sw t0, 0(t1) - wfi - -# Trap handler wrapper -# This is the point where the core jumps on any -# kind of trap (exception or interrupt). To safely -# call C code, we first need to save the core's -# context (caller-saved registers) on the stack. -# Before returning to the interrupted instruction -# (mret) we need to restore the context previously -# saved on the stack. -# The steps are: -# - Save state on stack -# - Read trap cause -# - Jump to generic C exception / interrupt handler -# - Restore state from stack -# - Return from interrupt (mret) -.align 2 -_trap_handler_wrapper: - addi sp, sp, -80 - sw ra, 72(sp) - sw t0, 64(sp) - sw t1, 56(sp) - sw t2, 48(sp) - sw a0, 40(sp) - sw a1, 32(sp) - sw a2, 24(sp) - sw a3, 16(sp) - sw a4, 8(sp) - sw a5, 0(sp) - # Read trap cause - # mcause[31] == 1'b1 -> Interrupt - # mcause[31] == 1'b0 -> Exception - csrr a0, mcause - blt a0, x0, _handle_interrupt - # Call user-defined exception handler - call croc_exception_handler - j _trap_exit - -_trap_exit: - lw ra, 72(sp) - lw t0, 64(sp) - lw t1, 56(sp) - lw t2, 48(sp) - lw a0, 40(sp) - lw a1, 32(sp) - lw a2, 24(sp) - lw a3, 16(sp) - lw a4, 8(sp) - lw a5, 0(sp) - addi sp, sp, 80 - mret - -_handle_interrupt: - # Clear interrupt flag bit - li t0, 1 - slli t0, t0, 31 # t0 = 1 << 31 - not t0, t0 # t0 = ~(1 << 31) - and a0, a0, t0 # a0 = a0 & 0x7FFF_FFFF - li t0, 7 # RISC-V M-mode timer interrupt ID is 7 - beq a0, t0, _clint_timer_handler - # Call user-defined interrupt handler - call croc_interrupt_handler - j _trap_exit - -# CLINT timer interrupt handler -# Disable timer interrupt and return -# Timer interrupt enable == bit 7 of mie CSR -_clint_timer_handler: - li t0, 1 - li t0, (1 << 7) - csrc mie, t0 - j _trap_exit + tail main -# Default exception handler -# Do nothing and return -# Define in C test to override +# ================================================================== +# Default exception handler (weak, override in C) +# ================================================================== +.section .text .weak croc_exception_handler +.globl croc_exception_handler croc_exception_handler: ret -# Default interrupt handler -# Do nothing and return -# Define in C test to override +# ================================================================== +# Default interrupt handler (weak, override in C) +# ================================================================== .weak croc_interrupt_handler +.globl croc_interrupt_handler croc_interrupt_handler: + li t0, 7 # timer interrupt ID + bne a0, t0, 1f # not timer? skip + li t0, (1 << 7) # MTIE bit + csrc mie, t0 # disable timer interrupt +1: ret diff --git a/sw/link.ld b/sw/link.ld index 590658c2..47f42871 100644 --- a/sw/link.ld +++ b/sw/link.ld @@ -19,6 +19,10 @@ SECTIONS { /DISCARD/ : { *(.riscv.attributes) *(.comment) } + .vectors : ALIGN(4) { + *(.vectors) + } >SRAM + .text._start : { *(.text._start) } >SRAM @@ -40,4 +44,11 @@ SECTIONS /* Global absolute symbols */ PROVIDE(__global_pointer$ = ADDR(.misc) + SIZEOF(.misc)/2); PROVIDE(__stack_pointer$ = ORIGIN(SRAM) + LENGTH(SRAM)); -PROVIDE(status = 0x03000008); + +/* Bootrom symbols (fixed addresses, regenerate with: make -C rtl/bootrom) */ +PROVIDE(_eoc = 0x02000100); +PROVIDE(_trap_handler_wrapper = 0x02000200); +PROVIDE(_trap_exit = 0x02000300); + +/* SoC register symbols */ +PROVIDE(corestatus = 0x03000008); From 81945f03752ce543b8f29e6629fb7aa4179497da Mon Sep 17 00:00:00 2001 From: Philippe Sauter Date: Wed, 4 Feb 2026 18:37:36 +0100 Subject: [PATCH 5/5] WIP: optimize and document bootrom --- README.md | 3 +- artistic/src/croc.json | 8 +- artistic/src/croc_map.json | 4 +- artistic/src/croc_modules.json | 4 + rtl/bootrom/.gitignore | 4 + rtl/bootrom/bootrom.S | 49 ++-- rtl/bootrom/bootrom.sv | 361 +++++++++++++++--------------- scripts/bootrom.sv.tpl | 149 ++++++++++++ scripts/gen_bootrom.py | 227 +++++++++++++++++++ yosys/scripts/yosys_synthesis.tcl | 1 + 10 files changed, 594 insertions(+), 216 deletions(-) create mode 100644 rtl/bootrom/.gitignore create mode 100644 scripts/bootrom.sv.tpl create mode 100644 scripts/gen_bootrom.py diff --git a/README.md b/README.md index d4edccc9..120bb9b3 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ The address map of the default configuration is as follows: | Start Address | Stop Address | Description | |-----------------|-----------------|--------------------------------------------| | `32'h0000_0000` | `32'h0004_0000` | Debug module (JTAG) | +| `32'h0200_0000` | `32'h0200_4000` | Bootrom | +| `32'h0200_4000` | `32'h0200_8000` | CLINT peripheral | | `32'h0300_0000` | `32'h0300_1000` | SoC control/info registers | | `32'h0300_2000` | `32'h0300_3000` | UART peripheral | | `32'h0300_5000` | `32'h0300_6000` | GPIO peripheral | @@ -62,7 +64,6 @@ The address map of the default configuration is as follows: | `32'h2000_0000` | `32'h8000_0000` | Passthrough to user domain | | `32'h2000_0000` | `32'h2000_1000` | reserved for string formatted user ROM* | - *If people modify Croc we suggest they add a ROM at this address containing additional information like the names of the developers, a project link or similar. This can then be written out via UART. We ask people to format the ROM like a C string with zero termination and using ASCII encoding if feasible. diff --git a/artistic/src/croc.json b/artistic/src/croc.json index 8763b00f..35418e3f 100644 --- a/artistic/src/croc.json +++ b/artistic/src/croc.json @@ -6,12 +6,12 @@ "file" : "../meerkat_work/croc_chip.gds.gz", "x_offset_um" : 0, "y_offset_um" : 0, - "width_um" : 1930, - "height_um" : 1930 + "width_um" : 2000, + "height_um" : 2000 }, "image": { - "px_width" : 965, - "px_height" : 965, + "px_width" : 1000, + "px_height" : 1000, "overrender_factor" : 4, "num_segs_width" : 1, "num_segs_height" : 1, diff --git a/artistic/src/croc_map.json b/artistic/src/croc_map.json index c6c52993..d397706a 100644 --- a/artistic/src/croc_map.json +++ b/artistic/src/croc_map.json @@ -4,8 +4,8 @@ }, "gds" : { "file" : "../meerkat_work/croc_chip.gds.gz", - "x_offset_um" : -59, - "y_offset_um" : -59, + "x_offset_um" : -24, + "y_offset_um" : -24, "width_um" : 2048, "height_um" : 2048 }, diff --git a/artistic/src/croc_modules.json b/artistic/src/croc_modules.json index eff93495..04871f1e 100644 --- a/artistic/src/croc_modules.json +++ b/artistic/src/croc_modules.json @@ -27,6 +27,10 @@ "name": "GPIO", "color": "#43fdaa" }, + "i_bootrom": { + "name": "ROM", + "color": "#8cf805" + }, "gen_sram_bank": { "name": "SRAM", "color": "#39729f" diff --git a/rtl/bootrom/.gitignore b/rtl/bootrom/.gitignore new file mode 100644 index 00000000..a93f2a2d --- /dev/null +++ b/rtl/bootrom/.gitignore @@ -0,0 +1,4 @@ +bootrom.bin +bootrom.dump +bootrom.elf +bootrom.o diff --git a/rtl/bootrom/bootrom.S b/rtl/bootrom/bootrom.S index 51311593..aa7b7469 100644 --- a/rtl/bootrom/bootrom.S +++ b/rtl/bootrom/bootrom.S @@ -36,21 +36,21 @@ .section .text .globl _start _start: - addi t0, zero, 8 # t0 = 8 (MSIE bit, bit 3) - csrw mie, t0 # Enable M-mode software interrupt - wfi # Sleep until interrupt - lui t0, 0x02040 # t0 = CLINT base (0x0204_0000) - sw zero, 0(t0) # Clear msip - csrw mie, zero # Disable all interrupts + csrwi mie, 8 # Enable M-mode software interrupt + wfi # Sleep until interrupt + lui t0, 0x02040 # t0 = CLINT base (0x0204_0000) + sw zero, 0(t0) # Clear msip + csrwi mie, 0 # Disable all interrupts # ----------------------------------------------------------------------------- # Register Initialization # Clear x1, x3-x15. Skip x2 stack pointer, set by main sw (needs SRAM size) # ----------------------------------------------------------------------------- - addi x1, zero, 0 +# addi x1, zero, 0 # x1 is ra, used later, so we can skip clearing it +# addi x2, zero, 0 # x2 is sp, set by SRAM stub, so we can skip clearing it addi x3, zero, 0 addi x4, zero, 0 - addi x5, zero, 0 +# addi x5, zero, 0 # x5 is t0, used later, so we can skip clearing it addi x6, zero, 0 addi x7, zero, 0 addi x8, zero, 0 @@ -73,10 +73,10 @@ _start: # Boot software # Read address from soc_ctrl register and jump # ----------------------------------------------------------------------------- - lui t0, 0x03000 # t0 = SOC_CTRL base (0x0300_0000) - lw t0, 0(t0) # t0 = boot address (BOOTADDR register) - la ra, _eoc # ra = &_eoc (return address for main) - jalr zero, 0(t0) # Jump to boot address (rd=zero, ra preserved) + lui t0, 0x03000 # t0 = SOC_CTRL base (0x0300_0000) + lw t0, 0(t0) # t0 = boot address (BOOTADDR register) + la ra, _eoc # ra = &_eoc (return address for main) + jalr zero, 0(t0) # Jump to boot address (rd=zero, ra preserved) # ----------------------------------------------------------------------------- # End of Computation (_eoc) @@ -86,11 +86,11 @@ _start: .org 0x100 .globl _eoc _eoc: - slli t0, a0, 1 # Shift return code left by 1 - ori t0, t0, 1 # Set bit 0 (EOC flag) - lui t1, 0x03000 # t1 = SOC_CTRL base (0x0300_0000) - sw t0, 8(t1) # Write to CORESTATUS (offset 0x08) - wfi # Halt + slli t0, a0, 1 # Shift return code left by 1 + ori t0, t0, 1 # Set bit 0 (EOC flag) + lui t1, 0x03000 # t1 = SOC_CTRL base (0x0300_0000) + sw t0, 8(t1) # Write to CORESTATUS (offset 0x08) + wfi # Halt # ----------------------------------------------------------------------------- # Trap Handler Wrapper @@ -114,13 +114,13 @@ _trap_handler_wrapper: # Read trap cause # mcause[31] == 1 -> Interrupt # mcause[31] == 0 -> Exception + lui t0, 0x10000 # t0 = SRAM base (0x1000_0000) csrr a0, mcause blt a0, zero, _handle_interrupt # Exception: call handler via SRAM vector table - lui t0, 0x10000 # t0 = SRAM base (0x1000_0000) - lw t0, 4(t0) # t0 = *(0x1000_0004) = exception handler - jalr ra, 0(t0) # Call exception handler + lw t1, 4(t0) # t1 = *(0x1000_0004) + jalr ra, 0(t1) # Call exception handler j _trap_exit # ----------------------------------------------------------------------------- @@ -128,11 +128,10 @@ _trap_handler_wrapper: # Strips mcause MSB to get interrupt ID, dispatches to user handler in SRAM # ----------------------------------------------------------------------------- _handle_interrupt: - slli a0, a0, 1 # Strip MSB (shift left) - srli a0, a0, 1 # Restore (shift right) -> a0 = interrupt ID - lui t0, 0x10000 # t0 = SRAM base (0x1000_0000) - lw t0, 8(t0) # t0 = *(0x1000_0008) = interrupt handler - jalr ra, 0(t0) # Call interrupt handler + slli a0, a0, 1 # Strip MSB (shift left) + srli a0, a0, 1 # Restore (shift right) -> a0 = interrupt ID + lw t1, 8(t0) # t1 = *(0x1000_0008) + jalr ra, 0(t1) # Call interrupt handler j _trap_exit # ----------------------------------------------------------------------------- diff --git a/rtl/bootrom/bootrom.sv b/rtl/bootrom/bootrom.sv index b1552234..136e2627 100644 --- a/rtl/bootrom/bootrom.sv +++ b/rtl/bootrom/bootrom.sv @@ -15,193 +15,186 @@ /// 4. Reads boot address from soc_ctrl, jumps there with jalr zero (ra = &_eoc) /// 5. On main() return: _eoc packs retval, writes CORESTATUS, halts /// Trap handler dispatches to SRAM function pointer table at 0x1000_0000. -/// Source: bootrom.S (regenerate with: make -C rtl/bootrom) +/// Source: bootrom.S module bootrom #( - /// The OBI configuration for all ports. - parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, - /// OBI request type - parameter type obi_req_t = logic, - /// OBI response type - parameter type obi_rsp_t = logic + /// The OBI configuration for all ports. + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + /// OBI request type + parameter type obi_req_t = logic, + /// OBI response type + parameter type obi_rsp_t = logic ) ( - input logic clk_i, - input logic rst_ni, - input obi_req_t obi_req_i, - output obi_rsp_t obi_rsp_o + input logic clk_i, + input logic rst_ni, + input obi_req_t obi_req_i, + output obi_rsp_t obi_rsp_o ); - //--------------------------------------------------------------------------------------------- - // Bootrom devided into blocks per contigous label - //--------------------------------------------------------------------------------------------- - // Contiguous block starting at 0x02000000: _start - localparam int unsigned StartRomWords = 28; - localparam logic [31:0] StartRom [StartRomWords] = '{ - // <_start> - 32'h00800293, // 0x02000000: addi x5,x0,8 - 32'h30429073, // 0x02000004: csrrw x0,mie,x5 - 32'h10500073, // 0x02000008: wfi - 32'h020402B7, // 0x0200000C: lui x5,0x2040 - 32'h0002A023, // 0x02000010: sw x0,0(x5) # 2040000 <__global_pointer$+0x3e4d0> - 32'h30401073, // 0x02000014: csrrw x0,mie,x0 - 32'h00000093, // 0x02000018: addi x1,x0,0 - 32'h00000193, // 0x0200001C: addi x3,x0,0 - 32'h00000213, // 0x02000020: addi x4,x0,0 - 32'h00000293, // 0x02000024: addi x5,x0,0 - 32'h00000313, // 0x02000028: addi x6,x0,0 - 32'h00000393, // 0x0200002C: addi x7,x0,0 - 32'h00000413, // 0x02000030: addi x8,x0,0 - 32'h00000493, // 0x02000034: addi x9,x0,0 - 32'h00000513, // 0x02000038: addi x10,x0,0 - 32'h00000593, // 0x0200003C: addi x11,x0,0 - 32'h00000613, // 0x02000040: addi x12,x0,0 - 32'h00000693, // 0x02000044: addi x13,x0,0 - 32'h00000713, // 0x02000048: addi x14,x0,0 - 32'h00000793, // 0x0200004C: addi x15,x0,0 - 32'h00000297, // 0x02000050: auipc x5,0x0 - 32'h1B028293, // 0x02000054: addi x5,x5,432 # 2000200 <_trap_handler_wrapper> - 32'h30529073, // 0x02000058: csrrw x0,mtvec,x5 - 32'h030002B7, // 0x0200005C: lui x5,0x3000 - 32'h0002A283, // 0x02000060: lw x5,0(x5) # 3000000 <__global_pointer$+0xffe4d0> - 32'h00000097, // 0x02000064: auipc x1,0x0 - 32'h09C08093, // 0x02000068: addi x1,x1,156 # 2000100 <_eoc> - 32'h00028067 // 0x0200006C: jalr x0,0(x5) - }; - - // Contiguous block starting at 0x02000100: _eoc - localparam int unsigned EocRomWords = 5; - localparam logic [31:0] EocRomData [EocRomWords] = '{ - // <_eoc> - 32'h00151293, // 0x02000100: slli x5,x10,0x1x00010413 - 32'h0012E293, // 0x02000104: ori x5,x5,1 - 32'h03000337, // 0x02000108: lui x6,0x3000 - 32'h00532423, // 0x0200010C: sw x5,8(x6) # 3000008 <__global_pointer$+0xffe4d8> - 32'h10500073 // 0x02000110: wfi - }; - - // Contiguous block starting at 0x02000200: _trap_handler_wrapper - localparam int unsigned TrapHandlerRomWords = 23; - localparam logic [31:0] TrapHandlerRomData [TrapHandlerRomWords] = '{ - // <_trap_handler_wrapper> - 32'hFB010113, // 0x02000200: addi x2,x2,-80 - 32'h04112423, // 0x02000204: sw x1,72(x2) - 32'h04512023, // 0x02000208: sw x5,64(x2) - 32'h02612C23, // 0x0200020C: sw x6,56(x2) - 32'h02712823, // 0x02000210: sw x7,48(x2) - 32'h02A12423, // 0x02000214: sw x10,40(x2) - 32'h02B12023, // 0x02000218: sw x11,32(x2) - 32'h00C12C23, // 0x0200021C: sw x12,24(x2) - 32'h00D12823, // 0x02000220: sw x13,16(x2) - 32'h00E12423, // 0x02000224: sw x14,8(x2) - 32'h00F12023, // 0x02000228: sw x15,0(x2) - 32'h34202573, // 0x0200022C: csrrs x10,mcause,x0 - 32'h00054A63, // 0x02000230: blt x10,x0,2000244 <_handle_interrupt> - 32'h100002B7, // 0x02000234: lui x5,0x10000 - 32'h0042A283, // 0x02000238: lw x5,4(x5) # 10000004 <__global_pointer$+0xdffe4d4> - 32'h000280E7, // 0x0200023C: jalr x1,0(x5) - 32'h0C00006F, // 0x02000240: jal x0,2000300 <_trap_exit> - // <_handle_interrupt> - 32'h00151513, // 0x02000244: slli x10,x10,0x1 - 32'h00155513, // 0x02000248: srli x10,x10,0x1 - 32'h100002B7, // 0x0200024C: lui x5,0x10000 - 32'h0082A283, // 0x02000250: lw x5,8(x5) # 10000008 <__global_pointer$+0xdffe4d8> - 32'h000280E7, // 0x02000254: jalr x1,0(x5) - 32'h0A80006F // 0x02000258: jal x0,2000300 <_trap_exit> - }; - - // Contiguous block starting at 0x02000300: _trap_exit - localparam int unsigned TrapExitRomWords = 12; - localparam logic [31:0] TrapExitRomData [TrapExitRomWords] = '{ - // <_trap_exit> - 32'h04812083, // 0x02000300: lw x1,72(x2) - 32'h04012283, // 0x02000304: lw x5,64(x2) - 32'h03812303, // 0x02000308: lw x6,56(x2) - 32'h03012383, // 0x0200030C: lw x7,48(x2) - 32'h02812503, // 0x02000310: lw x10,40(x2) - 32'h02012583, // 0x02000314: lw x11,32(x2) - 32'h01812603, // 0x02000318: lw x12,24(x2) - 32'h01012683, // 0x0200031C: lw x13,16(x2) - 32'h00812703, // 0x02000320: lw x14,8(x2) - 32'h00012783, // 0x02000324: lw x15,0(x2) - 32'h05010113, // 0x02000328: addi x2,x2,80 - 32'h30200073 // 0x0200032C: mret - }; - - - - // -------------------------------------------------------------------------- - // Handle OBI requests - // -------------------------------------------------------------------------- - localparam int unsigned WordAddressWidth = 10; // 12bit byte address - - logic we_d, we_q; - logic req_d, req_q; - logic [ObiCfg.IdWidth-1:0] id_d, id_q; - logic [WordAddressWidth-1:0] word_addr_d, word_addr_q; - - assign req_d = obi_req_i.req; - assign we_d = obi_req_i.a.we; - assign id_d = obi_req_i.a.aid; - assign word_addr_d = obi_req_i.a.addr[WordAddressWidth+2-1:2]; - - // Latch request for one-cycle response - `FF(req_q, req_d, '0, clk_i, rst_ni) - `FF(we_q, we_d, '0, clk_i, rst_ni) - `FF(id_q, id_d, '0, clk_i, rst_ni) - `FF(word_addr_q, word_addr_d, '0, clk_i, rst_ni) - - // -------------------------------------------------------------------------- - // Mask-based ROM decode: - // - upper 4 bits of the *word address* select the ROM - // - lower 6 bits select the word within that block - // -------------------------------------------------------------------------- - logic rom_req; - logic [ 3:0] rom_select; - logic [ 5:0] rom_idx, rom_size; - logic rom_error; - logic [31:0] rom_rdata; - - assign rom_req = req_q && !we_d; - assign rom_select = word_addr_q[WordAddressWidth-1 -: 4]; - assign rom_idx = word_addr_q[5:0]; - - always_comb begin - rom_rdata = 32'h0000_0000; - rom_error = 1'b1; - - case (rom_select) - 4'b0000: rom_size = StartRomWords; - 4'b0001: rom_size = EocRomWords; - 4'b0010: rom_size = TrapHandlerRomWords; - 4'b0011: rom_size = TrapExitRomWords; - default: rom_size = 8'h00; - endcase - - rom_error = (rom_idx >= rom_size); - - if (!rom_error && rom_req) begin - case (rom_select) - 4'b0000: rom_rdata = StartRom[rom_idx]; - 4'b0001: rom_rdata = EocRomData[rom_idx]; - 4'b0010: rom_rdata = TrapHandlerRomData[rom_idx]; - 4'b0011: rom_rdata = TrapExitRomData[rom_idx]; - default: rom_rdata = 32'h0000_0000; - endcase - end - end - - always_comb begin - obi_rsp_o = '0; - obi_rsp_o.gnt = 1'b1; // always grant - obi_rsp_o.rvalid = req_q; - obi_rsp_o.r.rid = id_q; - if (we_q) begin - // write request - obi_rsp_o.r.rdata = 32'hBADCAB1E; - obi_rsp_o.r.err = 1'b1; - end else begin - obi_rsp_o.r.rdata = rom_rdata; - obi_rsp_o.r.err = 1'b0; - end - end + //----------------------------------------------------------------------------------- + // Bootrom divided into blocks per contiguous label + //----------------------------------------------------------------------------------- + // Contiguous block starting at 0x02000000: _start + localparam int unsigned StartRomWords = 25; + localparam logic [31:0] StartRom [StartRomWords] = '{ + // <_start> + 32'h30445073, // 0x02000000: csrwi mie,8 + 32'h10500073, // 0x02000004: wfi + 32'h020402B7, // 0x02000008: lui t0,0x2040 + 32'h0002A023, // 0x0200000C: sw zero,0(t0) # 2040000 <__global_pointer$+0x3e4d0> + 32'h30405073, // 0x02000010: csrwi mie,0 + 32'h00000193, // 0x02000014: li gp,0 + 32'h00000213, // 0x02000018: li tp,0 + 32'h00000313, // 0x0200001C: li t1,0 + 32'h00000393, // 0x02000020: li t2,0 + 32'h00000413, // 0x02000024: li s0,0 + 32'h00000493, // 0x02000028: li s1,0 + 32'h00000513, // 0x0200002C: li a0,0 + 32'h00000593, // 0x02000030: li a1,0 + 32'h00000613, // 0x02000034: li a2,0 + 32'h00000693, // 0x02000038: li a3,0 + 32'h00000713, // 0x0200003C: li a4,0 + 32'h00000793, // 0x02000040: li a5,0 + 32'h00000297, // 0x02000044: auipc t0,0x0 + 32'h1BC28293, // 0x02000048: addi t0,t0,444 # 2000200 <_trap_handler_wrapper> + 32'h30529073, // 0x0200004C: csrw mtvec,t0 + 32'h030002B7, // 0x02000050: lui t0,0x3000 + 32'h0002A283, // 0x02000054: lw t0,0(t0) # 3000000 <__global_pointer$+0xffe4d0> + 32'h00000097, // 0x02000058: auipc ra,0x0 + 32'h0A808093, // 0x0200005C: addi ra,ra,168 # 2000100 <_eoc> + 32'h00028067 // 0x02000060: jr t0 + }; + + // Contiguous block starting at 0x02000100: _eoc + localparam int unsigned EocRomWords = 5; + localparam logic [31:0] EocRom [EocRomWords] = '{ + // <_eoc> + 32'h00151293, // 0x02000100: slli t0,a0,0x1 + 32'h0012E293, // 0x02000104: ori t0,t0,1 + 32'h03000337, // 0x02000108: lui t1,0x3000 + 32'h00532423, // 0x0200010C: sw t0,8(t1) # 3000008 <__global_pointer$+0xffe4d8> + 32'h10500073 // 0x02000110: wfi + }; + + // Contiguous block starting at 0x02000200: _trap_handler_wrapper + localparam int unsigned TrapHandlerRomWords = 22; + localparam logic [31:0] TrapHandlerRom [TrapHandlerRomWords] = '{ + // <_trap_handler_wrapper> + 32'hFB010113, // 0x02000200: addi sp,sp,-80 + 32'h04112423, // 0x02000204: sw ra,72(sp) + 32'h04512023, // 0x02000208: sw t0,64(sp) + 32'h02612C23, // 0x0200020C: sw t1,56(sp) + 32'h02712823, // 0x02000210: sw t2,48(sp) + 32'h02A12423, // 0x02000214: sw a0,40(sp) + 32'h02B12023, // 0x02000218: sw a1,32(sp) + 32'h00C12C23, // 0x0200021C: sw a2,24(sp) + 32'h00D12823, // 0x02000220: sw a3,16(sp) + 32'h00E12423, // 0x02000224: sw a4,8(sp) + 32'h00F12023, // 0x02000228: sw a5,0(sp) + 32'h100002B7, // 0x0200022C: lui t0,0x10000 + 32'h34202573, // 0x02000230: csrr a0,mcause + 32'h00054863, // 0x02000234: bltz a0,2000244 <_handle_interrupt> + 32'h0042A303, // 0x02000238: lw t1,4(t0) # 10000004 <__global_pointer$+0xdffe4d4> + 32'h000300E7, // 0x0200023C: jalr t1 + 32'h0C00006F, // 0x02000240: j 2000300 <_trap_exit> + 32'h00151513, // 0x02000244: slli a0,a0,0x1 + 32'h00155513, // 0x02000248: srli a0,a0,0x1 + 32'h0082A303, // 0x0200024C: lw t1,8(t0) + 32'h000300E7, // 0x02000250: jalr t1 + 32'h0AC0006F // 0x02000254: j 2000300 <_trap_exit> + }; + + // Contiguous block starting at 0x02000300: _trap_exit + localparam int unsigned TrapExitRomWords = 12; + localparam logic [31:0] TrapExitRom [TrapExitRomWords] = '{ + // <_trap_exit> + 32'h04812083, // 0x02000300: lw ra,72(sp) + 32'h04012283, // 0x02000304: lw t0,64(sp) + 32'h03812303, // 0x02000308: lw t1,56(sp) + 32'h03012383, // 0x0200030C: lw t2,48(sp) + 32'h02812503, // 0x02000310: lw a0,40(sp) + 32'h02012583, // 0x02000314: lw a1,32(sp) + 32'h01812603, // 0x02000318: lw a2,24(sp) + 32'h01012683, // 0x0200031C: lw a3,16(sp) + 32'h00812703, // 0x02000320: lw a4,8(sp) + 32'h00012783, // 0x02000324: lw a5,0(sp) + 32'h05010113, // 0x02000328: addi sp,sp,80 + 32'h30200073 // 0x0200032C: mret + }; + + // -------------------------------------------------------------------------- + // Handle OBI requests + // -------------------------------------------------------------------------- + localparam int unsigned WordAddressWidth = 10; // 12-bit byte address + + logic we_d, we_q; + logic req_d, req_q; + logic [ObiCfg.IdWidth-1:0] id_d, id_q; + logic [WordAddressWidth-1:0] word_addr_d, word_addr_q; + + assign req_d = obi_req_i.req; + assign we_d = obi_req_i.a.we; + assign id_d = obi_req_i.a.aid; + assign word_addr_d = obi_req_i.a.addr[WordAddressWidth+2-1:2]; + + // Latch request for one-cycle response + `FF(req_q, req_d, '0, clk_i, rst_ni) + `FF(we_q, we_d, '0, clk_i, rst_ni) + `FF(id_q, id_d, '0, clk_i, rst_ni) + `FF(word_addr_q, word_addr_d, '0, clk_i, rst_ni) + + // -------------------------------------------------------------------------- + // Mask-based ROM decode: + // - upper 4 bits of the *word address* select the ROM block + // - lower 6 bits index the word within that block + // -------------------------------------------------------------------------- + logic rom_req; + logic [ 3:0] rom_select; + logic [ 5:0] rom_idx, rom_size; + logic rom_error; + logic [31:0] rom_rdata; + + assign rom_req = req_q && !we_q; + assign rom_select = word_addr_q[WordAddressWidth-1 -: 4]; + assign rom_idx = word_addr_q[5:0]; + + always_comb begin + rom_rdata = 32'h0000_0000; + rom_error = 1'b1; + + case (rom_select) + 4'b0000: rom_size = StartRomWords; + 4'b0001: rom_size = EocRomWords; + 4'b0010: rom_size = TrapHandlerRomWords; + 4'b0011: rom_size = TrapExitRomWords; + default: rom_size = 6'h00; + endcase + + rom_error = (rom_idx >= rom_size); + + if (!rom_error && rom_req) begin + case (rom_select) + 4'b0000: rom_rdata = StartRom[rom_idx]; + 4'b0001: rom_rdata = EocRom[rom_idx]; + 4'b0010: rom_rdata = TrapHandlerRom[rom_idx]; + 4'b0011: rom_rdata = TrapExitRom[rom_idx]; + default: rom_rdata = 32'h0000_0000; + endcase + end + end + + always_comb begin + obi_rsp_o = '0; + obi_rsp_o.gnt = 1'b1; // always grant + obi_rsp_o.rvalid = req_q; + obi_rsp_o.r.rid = id_q; + if (we_q) begin + // write request + obi_rsp_o.r.rdata = 32'hBADCAB1E; + obi_rsp_o.r.err = 1'b1; + end else begin + obi_rsp_o.r.rdata = rom_rdata; + obi_rsp_o.r.err = rom_error; + end + end endmodule diff --git a/scripts/bootrom.sv.tpl b/scripts/bootrom.sv.tpl new file mode 100644 index 00000000..4f69021b --- /dev/null +++ b/scripts/bootrom.sv.tpl @@ -0,0 +1,149 @@ +<%doc> + bootrom.sv.tpl — Mako template for the bootrom SystemVerilog ROM module. + Rendered by scripts/gen_bootrom.py; edit this file, not bootrom.sv directly. + + Template variables: + sections list of dicts, one per ROM block (ordered by address), each with: + label (str) asm label that opens the block + sv_name (str) name of the localparam array + sv_size_name (str) name of the size constant (= sv_name + 'Words') + base_addr (int) absolute address of the first word in the block + words list of dicts, one per instruction word: + value (int) 32-bit instruction encoding + addr (int) absolute address of this word + comment (str) disassembly string, empty if unavailable + +<%! +# Module-level helpers: called as ${x8(n)} etc. throughout the template. +def x8(n): + """8-digit uppercase hex, e.g. 02000000.""" + return '{:08X}'.format(n) + +def b4(n): + """4-digit binary string for case select, e.g. 0010.""" + return '{:04b}'.format(n) +%> +// Copyright 2026 ETH Zurich and University of Bologna. +// Solderpad Hardware License, Version 0.51, see LICENSE for details. +// SPDX-License-Identifier: SHL-0.51 +// +// Authors: +// - Philippe Sauter + +`include "common_cells/registers.svh" + +/// Bootrom containing WFI trampoline, register init, trap handlers, and EOC. +/// After reset the core fetches from here: +/// 1. Enables MSIE, executes WFI, waits until woken by CLINT msip +/// 2. Clears msip, disables interrupts, clears registers x1/x3-x15 +/// 3. Sets mtvec to bootrom trap handler +/// 4. Reads boot address from soc_ctrl, jumps there with jalr zero (ra = &_eoc) +/// 5. On main() return: _eoc packs retval, writes CORESTATUS, halts +/// Trap handler dispatches to SRAM function pointer table at 0x1000_0000. +/// Source: bootrom.S +module bootrom #( + /// The OBI configuration for all ports. + parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, + /// OBI request type + parameter type obi_req_t = logic, + /// OBI response type + parameter type obi_rsp_t = logic +) ( + input logic clk_i, + input logic rst_ni, + input obi_req_t obi_req_i, + output obi_rsp_t obi_rsp_o +); + + //----------------------------------------------------------------------------------- + // Bootrom divided into blocks per contiguous label + //----------------------------------------------------------------------------------- +% for i, sec in enumerate(sections): + // Contiguous block starting at 0x${x8(sec['base_addr'])}: ${sec['label']} + localparam int unsigned ${sec['sv_size_name']} = ${len(sec['words'])}; + localparam logic [31:0] ${sec['sv_name']} [${sec['sv_size_name']}] = '{ + // <${sec['label']}> +% for j, w in enumerate(sec['words']): + 32'h${x8(w['value'])}${"" if j == len(sec['words'])-1 else ","} // 0x${x8(w['addr'])}${(': ' + w['comment']) if w['comment'] else ''} +% endfor + }; +% if i < len(sections) - 1: + +% endif +% endfor + + // -------------------------------------------------------------------------- + // Handle OBI requests + // -------------------------------------------------------------------------- + localparam int unsigned WordAddressWidth = 10; // 12-bit byte address + + logic we_d, we_q; + logic req_d, req_q; + logic [ObiCfg.IdWidth-1:0] id_d, id_q; + logic [WordAddressWidth-1:0] word_addr_d, word_addr_q; + + assign req_d = obi_req_i.req; + assign we_d = obi_req_i.a.we; + assign id_d = obi_req_i.a.aid; + assign word_addr_d = obi_req_i.a.addr[WordAddressWidth+2-1:2]; + + // Latch request for one-cycle response + `FF(req_q, req_d, '0, clk_i, rst_ni) + `FF(we_q, we_d, '0, clk_i, rst_ni) + `FF(id_q, id_d, '0, clk_i, rst_ni) + `FF(word_addr_q, word_addr_d, '0, clk_i, rst_ni) + + // -------------------------------------------------------------------------- + // Mask-based ROM decode: + // - upper 4 bits of the *word address* select the ROM block + // - lower 6 bits index the word within that block + // -------------------------------------------------------------------------- + logic rom_req; + logic [ 3:0] rom_select; + logic [ 5:0] rom_idx, rom_size; + logic rom_error; + logic [31:0] rom_rdata; + + assign rom_req = req_q && !we_q; + assign rom_select = word_addr_q[WordAddressWidth-1 -: 4]; + assign rom_idx = word_addr_q[5:0]; + + always_comb begin + rom_rdata = 32'h0000_0000; + rom_error = 1'b1; + + case (rom_select) +% for i, sec in enumerate(sections): + 4'b${b4(i)}: rom_size = ${sec['sv_size_name']}; +% endfor + default: rom_size = 6'h00; + endcase + + rom_error = (rom_idx >= rom_size); + + if (!rom_error && rom_req) begin + case (rom_select) +% for i, sec in enumerate(sections): + 4'b${b4(i)}: rom_rdata = ${sec['sv_name']}[rom_idx]; +% endfor + default: rom_rdata = 32'h0000_0000; + endcase + end + end + + always_comb begin + obi_rsp_o = '0; + obi_rsp_o.gnt = 1'b1; // always grant + obi_rsp_o.rvalid = req_q; + obi_rsp_o.r.rid = id_q; + if (we_q) begin + // write request + obi_rsp_o.r.rdata = 32'hBADCAB1E; + obi_rsp_o.r.err = 1'b1; + end else begin + obi_rsp_o.r.rdata = rom_rdata; + obi_rsp_o.r.err = rom_error; + end + end + +endmodule diff --git a/scripts/gen_bootrom.py b/scripts/gen_bootrom.py new file mode 100644 index 00000000..669fca07 --- /dev/null +++ b/scripts/gen_bootrom.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026 ETH Zurich and University of Bologna. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Authors: +# - Philippe Sauter +# +# Assemble a RISC-V ROM from an assembly source file and generate the +# corresponding SystemVerilog ROM module via a Mako template. +# +# The ROM is divided into contiguous blocks separated by .org directives in +# the assembly. Each block maps to one localparam array in the SV output. +# The decode logic selects a block using the upper word-address bits and the +# word within the block using the lower word-address bits. +# +# Usage (from repo root): python3 scripts/gen_bootrom.py +# Via Makefile: make -C rtl/bootrom + +import os +import re +import struct +import subprocess +import sys +from collections import namedtuple +from pathlib import Path + +# ============================================================================== +# Configuration — edit only in this section when adapting to a different ROM +# ============================================================================== + +# --- Toolchain ---------------------------------------------------------------- +# Override RISCV_PREFIX in the environment to use a different toolchain. +RISCV_PREFIX = os.environ.get('RISCV_PREFIX', 'riscv64-unknown-elf-') +MARCH = 'rv32i_zicsr' # -march flag passed to the assembler +LD_EMUL = 'elf32lriscv' # ld -m emulation (32-bit little-endian RISC-V) + +# --- Paths -------------------------------------------------------------------- +REPO_ROOT = Path(__file__).resolve().parents[1] +ROM_DIR = REPO_ROOT / 'rtl' / 'bootrom' +ROM_SRC = ROM_DIR / 'bootrom.S' # assembly source (input) +ROM_TPL = REPO_ROOT / 'scripts' / 'bootrom.sv.tpl' # Mako template (input) +ROM_SV = ROM_DIR / 'bootrom.sv' # SystemVerilog ROM module (output) + +# --- ROM address map ---------------------------------------------------------- +ROM_BASE = 0x02000000 # physical base address of the ROM + +# --- Block layout ------------------------------------------------------------- +# The ROM is split into fixed-size blocks using .org directives in the assembly. +# SECTION_STRIDE (in bytes) must equal the .org step and be a power of two. +# The SV decode uses log2(SECTION_STRIDE/4) lower word-address bits as the +# within-block index, and the remaining upper bits to select the block. +SECTION_STRIDE = 0x100 # bytes per block (= 64 words) + +# One entry per .org block in the assembly. To add or remove a block: +# 1. Add/remove the matching .org block in the assembly source. +# 2. Add/remove the corresponding row here. +# Fields: +# label - .globl label that starts the block in the assembly +# sv_name - name of the localparam array in the generated SV +# (the paired size constant is always sv_name + 'Words') +# offset - byte offset from ROM_BASE; must match the .org value +Section = namedtuple('Section', ['label', 'sv_name', 'offset']) + +SECTIONS = [ + Section('_start', 'StartRom', 0x000), + Section('_eoc', 'EocRom', 0x100), + Section('_trap_handler_wrapper', 'TrapHandlerRom',0x200), + Section('_trap_exit', 'TrapExitRom', 0x300), +] + +# ============================================================================== +# Implementation — no changes needed below for typical use cases +# ============================================================================== + +def _tool(name): + """Return the full path of a RISC-V toolchain binary.""" + return RISCV_PREFIX + name + + +def _run(cmd, **kwargs): + """Print and execute a shell command, raising on failure.""" + print(' ' + ' '.join(str(c) for c in cmd), file=sys.stderr) + subprocess.run([str(c) for c in cmd], check=True, **kwargs) + + +# ------------------------------------------------------------------------------ +# Build +# ------------------------------------------------------------------------------ + +def assemble(src, base_addr, march, ld_emul): + """Assemble src -> ELF -> binary + dump. Returns (elf, binary, dump) paths.""" + obj = src.with_suffix('.o') + elf = src.with_suffix('.elf') + binary = src.with_suffix('.bin') + dump = src.with_suffix('.dump') + + _run([_tool('as'), '-march', march, src, '-o', obj]) + _run([_tool('ld'), f'-m{ld_emul}', f'-Ttext=0x{base_addr:08X}', obj, '-o', elf]) + _run([_tool('objcopy'), '-O', 'binary', elf, binary]) + with open(dump, 'w') as f: + _run([_tool('objdump'), '-D', elf], stdout=f) + + return elf, binary, dump + + +# ------------------------------------------------------------------------------ +# Extraction +# ------------------------------------------------------------------------------ + +def get_disasm(elf): + """Return dict: absolute_address -> 'mnemonic operands' string.""" + result = subprocess.run( + [_tool('objdump'), '-d', str(elf)], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True + ) + # Line format: " 2000000:\t00800293 \taddi\tt0,zero,8" + pattern = re.compile(r'^\s*([0-9a-f]+):\s+[0-9a-f]+\s+(.+)$') + disasm = {} + for line in result.stdout.decode('utf-8').splitlines(): + m = pattern.match(line) + if m: + addr = int(m.group(1), 16) + insn = re.sub(r'\s+', ' ', m.group(2)).strip() + disasm[addr] = insn + return disasm + + +def read_section(binary_data, byte_offset, stride): + """Read words for one block, trimming trailing zero padding from .org fills.""" + words = [] + for i in range(stride // 4): + o = byte_offset + i * 4 + if o + 4 > len(binary_data): + break + (w,) = struct.unpack_from(' / <%! %> header blocks. + return rendered.lstrip('\n') + + +# ------------------------------------------------------------------------------ +# Main +# ------------------------------------------------------------------------------ + +def main(): + print('Assembling bootrom...', file=sys.stderr) + elf, binary, _ = assemble(ROM_SRC, ROM_BASE, MARCH, LD_EMUL) + + print('Extracting ROM data...', file=sys.stderr) + disasm = get_disasm(elf) + binary_data = binary.read_bytes() + + words_per_section = [] + for sec in SECTIONS: + words = read_section(binary_data, sec.offset, SECTION_STRIDE) + if not words: + print(f'WARNING: section {sec.label} at offset 0x{sec.offset:03X} is empty', + file=sys.stderr) + words_per_section.append(words) + + print(f'Rendering {ROM_TPL.name} -> {ROM_SV.name} ...', file=sys.stderr) + sections_data = prepare_template_data(SECTIONS, ROM_BASE, words_per_section, disasm) + ROM_SV.write_text(render_sv(ROM_TPL, sections_data)) + + total = sum(len(w) for w in words_per_section) + print(f'Done: {total} words ({total * 4} bytes) across {len(SECTIONS)} sections.', + file=sys.stderr) + for sec, words in zip(SECTIONS, words_per_section): + print(f' 0x{ROM_BASE + sec.offset:08X} {sec.label:<28} {len(words):3d} words', + file=sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/yosys/scripts/yosys_synthesis.tcl b/yosys/scripts/yosys_synthesis.tcl index 0070057e..a955a5df 100644 --- a/yosys/scripts/yosys_synthesis.tcl +++ b/yosys/scripts/yosys_synthesis.tcl @@ -40,6 +40,7 @@ yosys setattr -set keep_hierarchy 1 "t:dmi_jtag$*" yosys setattr -set keep_hierarchy 1 "t:dm_top$*" yosys setattr -set keep_hierarchy 1 "t:gpio$*" yosys setattr -set keep_hierarchy 1 "t:clint$*" +yosys setattr -set keep_hierarchy 1 "t:bootrom$*" yosys setattr -set keep_hierarchy 1 "t:obi_timer$*" yosys setattr -set keep_hierarchy 1 "t:reg_uart_wrap$*" yosys setattr -set keep_hierarchy 1 "t:soc_ctrl_regs$*"