LLHICJMHRWCZVTXZOJ3MO3LX6RCYUN6HTRMMJMGVOEBUXW6YMDWQC
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A simple dual-read, single-write register file
`default_nettype none
`timescale 1ps/1ps
module regfile
(
input bit clk, rst,
// Write port
input wire wen,
input wire [5:0] waddr,
input wire [31:0] wdata,
// Read ports
input wire [5:0] raddrA,
output wire [31:0] rdataA,
input wire [5:0] raddrB,
output wire [31:0] rdataB
);
// r0 is always 0, so we can not implement it
reg [31:0] registers [0:30];
always_ff @(posedge clk, rst)
if (rst)
for (integer i = 0; i < 32; i++) begin
registers[i] <= 0;
end
else if (wen)
registers[~waddr[4:0]] <= wdata;
assign rdataA = registers[~raddrA[4:0]];
assign rdataB = registers[~raddrB[4:0]];
endmodule
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A basic SOC with the core, ram, and uart
// Default 16KB ram
`default_nettype none
`timescale 1ps/1ps
module soc
#(
parameter integer MEM_WORDS = 4096
)
(
input bit clk,
input bit rst,
);
// TODO: impl uart
assign uart_tx = 0 & uart_rx & rst;
mem memory();
mem_impl memory_impl(clk, memory.impl);
// synthesis translate off
logic [1000:0] mem_file;
initial begin
if ($test$plusargs("trace") != 0) begin
$display("[%0t] Tracing to logs/vlt_dump.vcd...\n", $time);
$dumpfile("logs/vlt_dump.vcd");
$dumpvars();
end
end
// synthesis translate on
endmodule
input wire uart_rx,
output wire uart_tx
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A maxmimally simple core
`default_nettype none
`timescale 1ps/1ps
module basic_rv32i
(
mem.core mem,
`ifdef RISCV_FORMAL
output reg rvfi_valid,
output reg [63:0] rvfi_order,
output reg [31:0] rvfi_insn,
output reg rvfi_trap,
output reg rvfi_halt,
output reg rvfi_intr,
output reg [ 1:0] rvfi_mode,
output reg [ 1:0] rvfi_ixl,
output reg [ 4:0] rvfi_rs1_addr,
output reg [ 4:0] rvfi_rs2_addr,
output reg [31:0] rvfi_rs1_rdata,
output reg [31:0] rvfi_rs2_rdata,
output reg [ 4:0] rvfi_rd_addr,
output reg [31:0] rvfi_rd_wdata,
output reg [31:0] rvfi_pc_rdata,
output reg [31:0] rvfi_pc_wdata,
output reg [31:0] rvfi_mem_addr,
output reg [ 3:0] rvfi_mem_rmask,
output reg [ 3:0] rvfi_mem_wmask,
output reg [31:0] rvfi_mem_rdata,
output reg [31:0] rvfi_mem_wdata,
output reg [63:0] rvfi_csr_mcycle_rmask,
output reg [63:0] rvfi_csr_mcycle_wmask,
output reg [63:0] rvfi_csr_mcycle_rdata,
output reg [63:0] rvfi_csr_mcycle_wdata,
output reg [63:0] rvfi_csr_minstret_rmask,
output reg [63:0] rvfi_csr_minstret_wmask,
output reg [63:0] rvfi_csr_minstret_rdata,
output reg [63:0] rvfi_csr_minstret_wdata,
`endif
input bit clk, rst
);
endmodule
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A simple valid-ready memory interface that can handle a single memory transfer at a time
`default_nettype none
`timescale 1ps/1ps
// The core initiates a transfer by asserting valid, which stays high until the
// peer asserts ready. All core outputs are stable over the valid period.
// If the transfer is an instruction fetch, the core asserts instr.
interface mem;
bit valid;
bit ready;
bit instr;
enum bit[3:0] {
READ = 'b0000,
WRITE_BYTE0 = 'b0001,
WRITE_BYTE1 = 'b0010,
WRITE_BYTE2 = 'b0100,
WRITE_BYTE3 = 'b1000,
WRITE_HWORD0 = 'b0011,
WRITE_HWORD1 = 'b1100,
WRITE_WORD = 'b1111
} mode;
wire [31:0] addr;
wire [31:0] rdata;
wire [31:0] wdata;
modport core (
output valid,
input ready,
output instr,
output mode,
output addr,
input rdata,
output wdata
);
modport impl (
input valid,
output ready,
input instr,
input mode,
input addr,
output rdata,
input wdata
);
endinterface
/* verilator lint_off DECLFILENAME */
module mem_impl
#(
parameter integer MEM_WORDS = 4096
)
(
input bit clk,
mem.impl mem
);
always_ff @(posedge clk) begin
mem.ready <= '0;
if (mem.valid) begin
mem.rdata <= memory[mem.addr];
if (wen[0]) memory[mem.addr][ 7: 0] <= mem.wdata[ 7: 0];
if (wen[1]) memory[mem.addr][15: 8] <= mem.wdata[15: 8];
if (wen[2]) memory[mem.addr][23:16] <= mem.wdata[23:16];
if (wen[3]) memory[mem.addr][31:24] <= mem.wdata[31:24];
end
end
// synthesis translate off
logic [200:0] mem_file;
initial begin
if ($value$plusargs("mem", mem_file)) begin
$display("[%0t] Initializing memory\n", $time);
$readmemh(mem_file, memory);
end
end
phys_mem_addr: assert property(@(posedge clk) mem.addr < (4*MEM_WORDS));
// synthesis translate on
endmodule
reg [31:0] memory [0:MEM_WORDS-1];
reg [3:0] wen = mem.mode;
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A basic valid-ready ram, default 16KiB
`default_nettype none
`timescale 1ps/1ps
module uart
(
input logic clk_i,
input logic rst_ni,
input logic uart_i,
output logic uart_o
);
// TODO: implement
endmodule
// SPDX-License-Identifier: CERN-OHL-S-2.0
`default_nettype none
`timescale 1ps/1ps
package soc_pkg;
parameter int unsigned MEM_WORDS = 4096;
typedef struct packed {
logic valid;
logic [ 7:0] wen;
logic [63:0] addr;
logic [63:0] wdata;
} mem_in_t;
typedef struct packed {
logic ready;
logic [63:0] rdata;
} mem_out_t;
endpackage
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A basic SOC with the core, ram, and uart
// Default 32KiB ram
`default_nettype none
`timescale 1ps/1ps
module soc
(
input logic clk_i,
input logic rst_ni,
input logic uart_i,
output logic uart_o
);
uart uart_impl(.clk_i, .rst_ni, .uart_i, .uart_o);
soc_pkg::mem_in_t mem_in;
soc_pkg::mem_out_t mem_out;
mem ram(.clk_i, .core_i(mem_in), .core_o(mem_out));
`ifndef SYNTHESIS
initial begin
if ($test$plusargs("trace") != 0) begin
$display("[%0t] Tracing to logs/vlt_dump.vcd...\n", $time);
$dumpfile("logs/vlt_dump.vcd");
$dumpvars();
end
end
`endif
endmodule
// SPDX-License-Identifier: CERN-OHL-S-2.0
// A basic valid-ready ram, default 32KiB
`default_nettype none
`timescale 1ps/1ps
// The core initiates a transfer by asserting valid, which stays high until the
// peer asserts ready. All core outputs are stable over the valid period.
module mem
(
input logic clk_i,
input soc_pkg::mem_in_t core_i,
output soc_pkg::mem_out_t core_o
);
logic [63:0] memory [0:soc_pkg::MEM_WORDS-1];
always_ff @(posedge clk_i) begin
core_o.ready <= '0;
if (core_i.valid) begin
MemAddrFitsIntoPhys: assert(core_i.addr < (8*soc_pkg::MEM_WORDS));
core_o.rdata <= memory[core_i.addr];
core_o.ready <= 1'b1;
if (core_i.wen[0]) memory[core_i.addr][ 7: 0] <= core_i.wdata[ 7: 0];
if (core_i.wen[1]) memory[core_i.addr][15: 8] <= core_i.wdata[15: 8];
if (core_i.wen[2]) memory[core_i.addr][23:16] <= core_i.wdata[23:16];
if (core_i.wen[3]) memory[core_i.addr][31:24] <= core_i.wdata[31:24];
end
end
`ifndef SYNTHESIS
logic [200:0] mem_file;
initial begin
if ($value$plusargs("mem", mem_file)) begin
$display("[%0t] Initializing memory\n", $time);
$readmemh(mem_file, memory);
end
end
`endif
endmodule
// SPDX-License-Identifier: CERN-OHL-S-2.0
`default_nettype none
`timescale 1ps/1ps
package core_pkg;
// The core initiates a transfer by asserting valid, which stays high until the
// peer asserts ready. All core outputs are stable over the valid period.
typedef struct packed {
logic valid;
logic [ 3:0] wen;
logic [31:0] addr;
logic [31:0] wdata;
} core_out_t;
typedef struct packed {
logic ready;
logic [31:0] rdata;
} core_in_t;
endpackage
A toolchain will need to be compiled from source, as the default extensions for RISC-V toolchains are GC, and the `ilp32` ABI used is not compatible with the ABI they use.
A toolchain will need to be compiled from source, as the default extensions for RISC-V toolchains are GC, and the `lp64` ABI used is not compatible with the ABI they use.
VERILATOR_FLAGS += -j $(nproc)
VERILATOR_COMMON_FLAGS += -j $(JOBS)
# Check assertions
VERILATOR_COMMON_FLAGS += --assert
# Generate coverage analysis
VERILATOR_COMMON_FLAGS += --coverage
# Look in the rtl directory
VERILATOR_COMMON_FLAGS += -Irtl/soc -Irtl/core
VERILATOR_FLAGS += --output-split-cfuncs 50 --output-split-ctrace 50
# Warn about lint issues
VERILATOR_FLAGS += -Wall
# Check assertions
VERILATOR_FLAGS += --assert
# Generate coverage analysis
VERILATOR_FLAGS += --coverage
VERILATOR_GENERATION_FLAGS += --output-split-cfuncs 50 --output-split-ctrace 50
$(MAKE) -j -C obj_dir -f Vsoc.mk
$(MAKE) -j $(JOBS) -C obj_dir -f Vsoc.mk
soc-lint-verilator:
$(VERILATOR) $(VERILATOR_COMMON_FLAGS) --lint-only -Wall -Wpedantic $(SOC_RTLS)
soc-lint-slang:
$(SLANG) -j $(JOBS) -Irtl/soc -Irtl/core --allow-dup-initial-drivers $(SOC_RTLS)