Initial commit

This commit is contained in:
2025-09-03 14:56:28 +03:00
commit 3087975833
28 changed files with 1550 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
/obj_dir
/*.log
/log
+33
View File
@@ -0,0 +1,33 @@
SRC_ALL=$(wildcard src/**/*.sv)
SRC_MODULE=$(filter-out %_top.sv,$(filter-out %_tb.sv,$(SRC_ALL)))
DEF_MODULE=$(wildcard src/*.svh)
FIRMWARE_SRC=firmware/src/entry.S \
firmware/src/main.rs \
firmware/link.ld \
firmware/Cargo.toml
MAIN_TOP=src/rvx0_soc_top.sv
all: obj_dir/rvx0
obj_dir/apb0_memory.hex: obj_dir/firmware.bin tool/makebin.py
./tool/makebin.py obj_dir/firmware.bin $@
obj_dir/firmware.bin: firmware/target/riscv32i-unknown-none-elf/release/firmware
llvm-objcopy -O binary $< $@
firmware/target/riscv32i-unknown-none-elf/release/firmware: $(FIRMWARE_SRC)
cd firmware && cargo build --release
obj_dir/rvx0: $(SRC_MODULE) $(DEF_MODULE) $(MAIN_TOP) obj_dir/apb0_memory.hex
mkdir -p obj_dir
verilator --assert --binary -Isrc -o rvx0 $(MAIN_TOP) $(SRC_MODULE)
#
# obj_dir/firmware.elf: $(FIRMWARE_OBJ) src/firmware.ld
#
# obj_dir/apb0_memory.hex: src/apb0_memory.S tool/makebin.py
# mkdir -p obj_dir
# riscv64-elf-gcc -c -o obj_dir/apb0_memory.o src/apb0_memory.S
# llvm-objcopy -O binary obj_dir/apb0_memory.o obj_dir/apb0_memory.bin
# ./tool/makebin.py obj_dir/apb0_memory.bin $@
+6
View File
@@ -0,0 +1,6 @@
[target.riscv32i-unknown-none-elf]
rustflags = ["-Clink-arg=-Tlink.ld"]
[build]
target = "riscv32i-unknown-none-elf"
[unstable]
build-std = ["core"]
+1
View File
@@ -0,0 +1 @@
/target
+7
View File
@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "firmware"
version = "0.1.0"
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "firmware"
version = "0.1.0"
edition = "2024"
[dependencies]
+35
View File
@@ -0,0 +1,35 @@
ENTRY(__entry);
SECTIONS {
. = 0x1000;
.text : {
*(.text.entry)
*(.text*)
}
. = ALIGN(4);
.rodata : {
*(.rodata*)
. = ALIGN(4);
}
. = 0x20000;
.vectors : {
*(.text.vectors)
}
. = 0x20000000;
.data : {
*(.data*)
}
. = ALIGN(16);
.bss : {
*(.bss*)
}
}
+22
View File
@@ -0,0 +1,22 @@
.global __entry
.pushsection .text.entry
.option push
.option norvc
__entry:
la sp, stack_top
jal {firmware_main}
.option pop
.popsection // .text.entry
.pushsection .text.vectors
__vector:
j .
.popsection // .text.vectors
.pushsection .bss
stack_bottom:
.skip 2048
stack_top:
.popsection // .bss
+42
View File
@@ -0,0 +1,42 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
#![no_main]
use core::{arch::global_asm, fmt, panic::PanicInfo};
#[panic_handler]
fn panic_handler(_pi: &PanicInfo) -> ! {
loop {}
}
struct D;
const DEBUG_BASE: usize = 0x1000_0000;
impl fmt::Write for D {
fn write_str(&mut self, s: &str) -> fmt::Result {
let ptr: *mut u32 = core::ptr::with_exposed_provenance_mut(DEBUG_BASE);
for byte in s.bytes() {
unsafe { ptr.write_volatile(byte as u32) };
}
Ok(())
}
}
unsafe extern "C" fn firmware_main() -> ! {
use fmt::Write;
let mut d = D;
for i in 0..10 {
write!(d, "Hello {i}").ok();
write!(d, "\n").ok();
}
loop {
// TODO pause/system/fence instruction not implemented
// core::hint::spin_loop();
core::arch::asm!("nop");
}
}
global_asm!(include_str!("entry.S"), firmware_main = sym firmware_main);
+40
View File
@@ -0,0 +1,40 @@
module rvx0_alu (
input wire [3:0] aluop_i,
input wire [31:0] value1_i,
input wire [31:0] value2_i,
input wire [31:0] imm_i,
output logic [31:0] output_o
);
wire [31:0] value1;
wire [31:0] value2;
wire [4:0] value2_lo;
wire signed [31:0] value1_signed;
wire signed [31:0] value2_signed;
assign value1 = value1_i;
assign value2 = value2_i + imm_i;
assign value1_signed = value1;
assign value2_signed = value2;
assign value2_lo = value2[4:0];
always_comb begin
case (aluop_i)
ALUOP_ADD: output_o = value1 + value2;
ALUOP_SLL: output_o = value1 << value2_lo;
ALUOP_SLT: output_o = 32'(value1_signed < value2_signed);
ALUOP_SLTU: output_o = 32'(value1 < value2);
ALUOP_XOR: output_o = value1 ^ value2;
ALUOP_SRL: output_o = value1 >> value2_lo;
ALUOP_OR: output_o = value1 | value2;
ALUOP_AND: output_o = value1 & value2;
ALUOP_SUB: output_o = value1 - value2;
ALUOP_SRA: output_o = value1 >>> value2_lo;
endcase
end
endmodule
+28
View File
@@ -0,0 +1,28 @@
`include "rvx0_defs.svh"
module rvx0_brancher (
input wire [31:0] rs1_value_i,
input wire [31:0] rs2_value_i,
input wire [2:0] funct3_i,
output logic branch_taken_o
);
wire signed [31:0] rs1_value_signed;
wire signed [31:0] rs2_value_signed;
assign rs1_value_signed = rs1_value_i;
assign rs2_value_signed = rs2_value_i;
always_comb begin
case (funct3_i)
COND_EQ: branch_taken_o = rs1_value_i == rs2_value_i;
COND_NE: branch_taken_o = rs1_value_i != rs2_value_i;
COND_LT: branch_taken_o = rs1_value_signed < rs2_value_signed;
COND_GE: branch_taken_o = rs1_value_signed >= rs2_value_signed;
COND_LTU: branch_taken_o = rs1_value_i < rs2_value_i;
COND_GEU: branch_taken_o = rs1_value_i >= rs2_value_i;
default: branch_taken_o = 0;
endcase
end
endmodule
+133
View File
@@ -0,0 +1,133 @@
`include "rvx0_defs.svh"
module rvx0_control (
input wire clk_i,
input wire rst_i,
input wire need_memory_read_i,
input wire need_memory_write_i,
input wire exec_stall_i,
input wire exec_ready_i,
input wire instruction_fault_i,
input wire intr_ext_i,
input wire [31:0] memory_address_i,
input wire [31:0] memory_write_i,
input wire [31:0] pc_value_i,
rvx0_apb.master apb_master,
output reg [1:0] control_state_o,
output reg [31:0] instruction_o,
output reg [31:0] memory_read_o,
output reg fault_instruction_align_o,
output reg fault_instruction_fetch_o,
output reg fault_memory_access_o
);
always @(posedge clk_i) begin
if (rst_i) begin
control_state_o <= CSTATE_FETCH;
end
end
// TODO handle pslverr
always @(negedge clk_i) begin
if (~rst_i) begin
case (control_state_o)
CSTATE_FETCH: begin
fault_instruction_fetch_o <= 0;
fault_instruction_align_o <= 0;
if (intr_ext_i) begin
control_state_o <= CSTATE_EXEC;
instruction_o <= 0;
end else if (pc_value_i[1:0] != 0) begin
fault_instruction_align_o <= 1;
instruction_o <= 0;
control_state_o <= CSTATE_EXEC;
end else begin
apb_master.paddr <= pc_value_i;
apb_master.pwrite <= 0;
apb_master.penable <= 1;
if (apb_master.pslverr) begin
instruction_o <= 0;
fault_instruction_fetch_o <= 1;
apb_master.penable <= 0;
control_state_o <= CSTATE_EXEC;
end else if (apb_master.pready) begin
instruction_o <= apb_master.prdata;
apb_master.penable <= 0;
control_state_o <= CSTATE_READ;
end
end
end
CSTATE_READ: begin
fault_memory_access_o <= 0;
if (intr_ext_i) begin
control_state_o <= CSTATE_EXEC;
end else if (~need_memory_read_i || instruction_fault_i) begin
control_state_o <= CSTATE_EXEC;
end else begin
apb_master.paddr <= memory_address_i;
apb_master.pwrite <= 0;
apb_master.penable <= 1;
if (apb_master.pslverr) begin
memory_read_o <= 0;
apb_master.penable <= 0;
fault_memory_access_o <= 1;
control_state_o <= CSTATE_EXEC;
end else if (apb_master.pready) begin
memory_read_o <= apb_master.prdata;
apb_master.penable <= 0;
control_state_o <= CSTATE_EXEC;
end
end
end
CSTATE_EXEC: begin
if (exec_stall_i) begin
$display("control: exec_stall_i @ %x", pc_value_i);
control_state_o <= CSTATE_FETCH;
end else if (exec_ready_i) begin
control_state_o <= CSTATE_WRITE;
end
end
CSTATE_WRITE: begin
fault_memory_access_o <= 0;
if (~need_memory_write_i) begin
control_state_o <= CSTATE_FETCH;
end else begin
apb_master.paddr <= memory_address_i;
apb_master.pwdata <= memory_write_i;
apb_master.pwrite <= 1;
apb_master.penable <= 1;
$display("WRITE %x -> %x", memory_write_i, memory_address_i);
if (apb_master.pslverr) begin
apb_master.penable <= 0;
fault_memory_access_o <= 1;
control_state_o <= CSTATE_EXEC;
end else if (apb_master.pready) begin
apb_master.penable <= 0;
control_state_o <= CSTATE_FETCH;
end
end
end
endcase
end
end
endmodule
+301
View File
@@ -0,0 +1,301 @@
/* verilator lint_off PINMISSING */
/* verilator lint_off MULTIDRIVEN */
`include "rvx0_defs.svh"
module rvx0_core #(
parameter RESET_VECTOR
) (
input wire clk_i,
input wire rst_i,
input wire intr_ext_i,
rvx0_apb.master apb_master
);
wire [31:0] pc_value;
logic [31:0] pc_write_value;
wire fault;
// CSRs
wire [31:0] csr_mtvec;
wire [31:0] csr_mepc;
// Exception generation
logic [31:0] mcause_write_value;
logic [31:0] mepc_write_value;
logic [31:0] mtval_write_value;
// Faults
wire fault_memory_align;
wire fault_instruction;
wire fault_instruction_align;
wire fault_instruction_fetch;
wire fault_memory_access;
// Generated faults
wire fault_instruction_decode;
wire fault_unsupported_size;
wire fault_address_align;
// Control
wire need_memory_read;
wire need_memory_write;
logic exec_ready;
logic exec_stall;
wire [1:0] control_state;
wire [31:0] instruction;
wire state_exec;
// Regfile/control
wire [31:0] rs1_value;
wire [31:0] rs2_value;
logic [31:0] rd_write_value;
// Decoder
wire conditional;
wire [4:0] rs1;
wire [4:0] rs2;
wire [4:0] rd;
wire v1src;
wire [1:0] v2src;
wire [1:0] vdsrc;
wire [31:0] imm;
wire [3:0] aluop;
wire write_pc;
wire [2:0] funct3;
// Into ALU
wire [31:0] alu_value1;
logic [31:0] alu_value2;
wire [31:0] alu_output;
// Branch unit
wire write_pc_and_taken;
wire branch_taken;
wire write_rd;
wire pc_clk;
// Memword
wire [31:0] memory_read_input;
wire [31:0] memory_read_output;
wire [31:0] memory_write_input;
wire [31:0] memory_write_output;
wire memory_access_partial;
rvx0_pc #(
.RESET_VECTOR (RESET_VECTOR)
) pc (
.rst_i (rst_i),
.clk_i (pc_clk),
.pc_value_i (pc_write_value),
.write_pc_i (write_pc_and_taken),
.pc_value_o (pc_value)
);
rvx0_brancher brancher (
.rs1_value_i (rs1_value),
.rs2_value_i (rs2_value),
.funct3_i (funct3),
.branch_taken_o (branch_taken)
);
rvx0_decoder decoder (
.instruction_i (instruction),
.v1src_o (v1src),
.v2src_o (v2src),
.vdsrc_o (vdsrc),
.rs1_o (rs1),
.rs2_o (rs2),
.rd_o (rd),
.imm_o (imm),
.aluop_o (aluop),
.write_mem_o (need_memory_write),
.write_pc_o (write_pc),
.funct3_o (funct3),
.instruction_fault_o (fault_instruction_decode),
.conditional_o (conditional)
);
rvx0_regfile regfile (
.clk_i (clk_i),
.rst_i (rst_i),
.rs1_i (rs1),
.rs2_i (rs2),
.rd_i (rd),
.write_value_i (rd_write_value),
.rd_write_i (write_rd),
.rs1_o (rs1_value),
.rs2_o (rs2_value)
);
rvx0_alu alu (
.aluop_i (aluop),
.value1_i (alu_value1),
.value2_i (alu_value2),
.imm_i (imm),
.output_o (alu_output)
);
// Control
rvx0_control control (
.clk_i (clk_i),
.rst_i (rst_i),
.apb_master (apb_master),
.control_state_o (control_state),
.instruction_o (instruction),
.instruction_fault_i (fault_instruction),
.pc_value_i (pc_value),
.memory_address_i (alu_output),
.need_memory_read_i (need_memory_read),
.need_memory_write_i (need_memory_write),
.exec_ready_i (exec_ready),
.exec_stall_i (exec_stall),
.memory_read_o (memory_read_input),
.memory_write_i (memory_write_output),
.fault_instruction_align_o (fault_instruction_align),
.fault_instruction_fetch_o (fault_instruction_fetch),
.fault_memory_access_o (fault_memory_access)
);
rvx0_memword memword (
.memory_read_i (memory_read_input),
.memory_write_i (memory_write_input),
.memory_address_i (alu_output),
.funct3_i (funct3),
.memory_read_o (memory_read_output),
.memory_write_o (memory_write_output),
.memory_access_partial_o (memory_access_partial),
.alignment_fault_o (fault_address_align),
.unsupported_size_fault_o (fault_unsupported_size)
);
rvx0_csr csr (
.clk_i (clk_i),
.rst_i (rst_i),
.mepc_i (mepc_write_value),
.mepc_write_i (fault),
.mcause_i (mcause_write_value),
.mcause_write_i (fault),
.mtval_i (mtval_write_value),
.mtval_write_i (fault),
.mtvec_o (csr_mtvec)
);
assign fault = fault_instruction_align ||
fault_instruction_fetch ||
fault_instruction ||
fault_memory_align ||
fault_memory_access ||
intr_ext_i;
assign fault_instruction = fault_instruction_decode || (fault_unsupported_size && (need_memory_read || need_memory_write));
assign fault_memory_align = fault_address_align && (need_memory_write || need_memory_read);
assign memory_write_input = rs2_value;
assign need_memory_read = (vdsrc == RDSRC_MEM) || (need_memory_write && memory_access_partial);
assign alu_value1 = v1src == V1SRC_ZERO ? 0 : rs1_value;
assign state_exec = control_state == CSTATE_EXEC;
assign pc_clk = (state_exec || rst_i) && clk_i;
assign write_rd = (vdsrc != RDSRC_NONE) && state_exec && ~fault;
assign write_pc_and_taken = ((conditional ? branch_taken : 1) && write_pc) || fault;
// Value mux
always_comb begin
case (vdsrc)
RDSRC_PC: rd_write_value = pc_value + 4;
RDSRC_ALU: rd_write_value = alu_output;
RDSRC_MEM: rd_write_value = memory_read_output;
RDSRC_NONE: rd_write_value = 0;
endcase
case (v2src)
V2SRC_ZERO: alu_value2 = 0;
V2SRC_PC: alu_value2 = pc_value;
V2SRC_RS2: alu_value2 = rs2_value;
endcase
if (fault) begin
pc_write_value = csr_mtvec;
end else begin
pc_write_value = alu_output;
end
end
// Exception parameter encoding
always_comb begin
mcause_write_value = 0;
mtval_write_value = 0;
mepc_write_value = pc_value;
if (fault_instruction_align) begin
mcause_write_value = MCAUSE_INSTRUCTION_ADDRESS_MISALIGN;
end else if (fault_instruction_fetch) begin
mcause_write_value = MCAUSE_INSTRUCTION_ACCESS_FAULT;
mtval_write_value = pc_value;
end else if (fault_instruction) begin
mcause_write_value = MCAUSE_ILLEGAL_INSTRUCTION;
mtval_write_value = instruction;
end else if (~need_memory_write && fault_memory_align) begin
mcause_write_value = MCAUSE_LOAD_ADDRESS_MISALIGNED;
mtval_write_value = alu_output;
end else if (~need_memory_write && fault_memory_access) begin
mcause_write_value = MCAUSE_LOAD_ACCESS_FAULT;
end else if (fault_memory_align) begin
mcause_write_value = MCAUSE_STORE_ADDRESS_MISALIGNED;
mtval_write_value = alu_output;
end else if (fault_memory_access) begin
mcause_write_value = MCAUSE_STORE_ACCESS_FAULT;
// Because this happens at write pipeline stage and PC has already
// been incremented
mepc_write_value = pc_value - 4;
mtval_write_value = alu_output;
end else if (intr_ext_i) begin
mcause_write_value = MCAUSE_INTR_EXT_MMODE | MCAUSE_INTERRUPT;
end
end
always @(posedge clk_i) begin
case (control_state)
CSTATE_EXEC: begin
exec_ready = 0;
exec_stall = 0;
if (fault) begin
$display("Take exception mcause=%x, mepc=%x, mtval=%x to %x", mcause_write_value, mepc_write_value, mtval_write_value, csr_mtvec);
regfile.dump_state;
$finish;
exec_stall = 1;
end else begin
`ifdef RVX0_DEBUGGING
$display("[%x] %x, rs1 (r%d)=%x, rs2 (r%d)=%x, v1src=%d, v2src=%d, vdsrc=%d", pc_value, instruction, rs1, rs1_value, rs2, rs2_value, v1src, v2src, vdsrc);
$display("\talu_value1=%x, alu_value2=%x, imm=%x, rd_write_value=%x", alu_value1, alu_value2, imm, rd_write_value);
$display("\tmri=%x, mro=%x, mwi=%x, mwo=%x", memory_read_input, memory_read_output, memory_write_input, memory_write_output);
`endif
exec_ready = 1;
end
end
endcase
end
endmodule
+52
View File
@@ -0,0 +1,52 @@
// TODO Zicsr
module rvx0_csr (
input wire clk_i,
input wire rst_i,
input wire [31:0] mepc_i,
input wire [31:0] mcause_i,
input wire [31:0] mtval_i,
input wire mepc_write_i,
input wire mcause_write_i,
input wire mtval_write_i,
output wire [31:0] misa_o, // Hardwired
output wire [31:0] mvendorid_o, // Hardwired
output wire [31:0] marchid_o, // Hardwired
output wire [31:0] mimpid_o, // Hardwired
output wire [31:0] mhartid_o, // Hardwired
output reg [31:0] mstatus_o, // TODO
output reg [31:0] mstatush_o, // TODO
output reg [31:0] mtvec_o,
output reg [31:0] mepc_o,
output reg [31:0] mcause_o,
output reg [31:0] mtval_o // TODO
);
// Add extensions here :)
assign misa_o = MISA_MXLEN_32;
assign mvendorid_o = 0;
assign marchid_o = 0;
assign mimpid_o = 0;
assign mhartid_o = 0;
always @(posedge clk_i) begin
if (rst_i) begin
// TODO set from CPU
mtvec_o = 'h00020000;
mepc_o = 0;
mstatus_o = 0;
mstatush_o = 0;
end
if (mepc_write_i) begin
mepc_o <= mepc_i;
end
if (mcause_write_i) begin
mcause_o <= mcause_i;
end
if (mtval_write_i) begin
mtval_o <= mtval_i;
end
end
endmodule
+70
View File
@@ -0,0 +1,70 @@
module rvx0_memword (
input wire [31:0] memory_read_i,
input wire [31:0] memory_write_i,
input wire [31:0] memory_address_i,
input wire [2:0] funct3_i,
output logic [31:0] memory_write_o,
output logic [31:0] memory_read_o,
output wire memory_access_partial_o,
output logic alignment_fault_o,
output logic unsupported_size_fault_o
);
wire sign_extend;
wire [4:0] shift;
wire signed [31:0] memory_read_signed;
wire [31:0] wmask_8b;
wire [31:0] wmask_16b;
logic [31:0] memory_read_unsigned;
assign sign_extend = ~funct3_i[2];
assign shift = memory_address_i[1:0] << 3;
assign memory_access_partial_o = funct3_i[1:0] != 2'b10;
assign memory_read_signed = memory_read_unsigned;
assign wmask_8b = 32'hFF << shift;
assign wmask_16b = 32'hFFFF << shift;
always_comb begin
memory_read_unsigned = 0;
memory_write_o = 0;
alignment_fault_o = 0;
unsupported_size_fault_o = 0;
case (funct3_i[1:0])
2'b00: begin
memory_read_unsigned = (memory_read_i >> shift) & 32'hFF;
memory_write_o = (memory_read_i & ~wmask_8b) | ((memory_write_i & 32'hFF) << shift);
end
2'b01: begin
memory_read_unsigned = (memory_read_i >> shift) & 32'hFFFF;
memory_write_o = (memory_read_i & ~wmask_16b) | ((memory_write_i & 32'hFFFF) << shift);
if (memory_address_i[0] != 0) begin
alignment_fault_o = 1;
end
end
2'b10: begin
memory_read_unsigned = memory_read_i;
memory_write_o = memory_write_i;
if (memory_address_i[1:0] != 0) begin
alignment_fault_o = 1;
end
end
2'b11: begin
unsupported_size_fault_o = 1;
end
endcase
if (sign_extend) begin
memory_read_o = memory_read_signed;
end else begin
memory_read_o = memory_read_unsigned;
end
end
endmodule
+20
View File
@@ -0,0 +1,20 @@
module rvx0_pc #(
parameter logic [31:0] RESET_VECTOR
) (
input wire clk_i,
input wire rst_i,
input wire write_pc_i,
input wire [31:0] pc_value_i,
output reg [31:0] pc_value_o
);
always @(posedge clk_i) begin
if (rst_i) begin
pc_value_o <= RESET_VECTOR;
end else if (write_pc_i) begin
pc_value_o <= pc_value_i;
end else begin
pc_value_o <= pc_value_o + 4;
end
end
endmodule
+108
View File
@@ -0,0 +1,108 @@
module rvx0_regfile (
input wire clk_i,
input wire rst_i,
input wire [4:0] rs1_i,
input wire [4:0] rs2_i,
input wire [4:0] rd_i,
input wire [31:0] write_value_i,
input wire rd_write_i,
output wire [31:0] rs1_o,
output wire [31:0] rs2_o
);
// Debugger friendly names
wire [31:0] ra;
wire [31:0] sp;
wire [31:0] gp;
wire [31:0] tp;
wire [31:0] t0;
wire [31:0] t1;
wire [31:0] t2;
wire [31:0] s0;
wire [31:0] s1;
wire [31:0] a0;
wire [31:0] a1;
wire [31:0] a2;
wire [31:0] a3;
wire [31:0] a4;
wire [31:0] a5;
wire [31:0] a6;
wire [31:0] a7;
wire [31:0] s2;
wire [31:0] s3;
wire [31:0] s4;
wire [31:0] s5;
wire [31:0] s6;
wire [31:0] s7;
wire [31:0] s8;
wire [31:0] s9;
wire [31:0] s10;
wire [31:0] s11;
wire [31:0] t3;
wire [31:0] t4;
wire [31:0] t5;
wire [31:0] t6;
assign ra = regs[0];
assign sp = regs[1];
assign gp = regs[2];
assign tp = regs[3];
assign t0 = regs[4];
assign t1 = regs[5];
assign t2 = regs[6];
assign s0 = regs[7];
assign s1 = regs[8];
assign a0 = regs[9];
assign a1 = regs[10];
assign a2 = regs[11];
assign a3 = regs[12];
assign a4 = regs[13];
assign a5 = regs[14];
assign a6 = regs[15];
assign a7 = regs[16];
assign s2 = regs[17];
assign s3 = regs[18];
assign s4 = regs[19];
assign s5 = regs[20];
assign s6 = regs[21];
assign s7 = regs[22];
assign s8 = regs[23];
assign s9 = regs[24];
assign s10 = regs[25];
assign s11 = regs[26];
assign t3 = regs[27];
assign t4 = regs[28];
assign t5 = regs[29];
assign t6 = regs[30];
function void dump_state;
$display("ra=%x sp=%x gp=%x tp=%x", ra, sp, gp, tp);
$display("t0=%x t1=%x t2=%x s0=%x", t0, t1, t2, s0);
$display("s1=%x a0=%x a1=%x a2=%x", s1, a0, a1, a2);
$display("a3=%x a4=%x a5=%x a6=%x", a3, a4, a5, a6);
$display("a7=%x s2=%x s3=%x s4=%x", a7, s2, s3, s4);
$display("s5=%x s6=%x s7=%x s8=%x", s5, s6, s7, s8);
$display("s9=%x s10=%x s11=%x t3=%x", s9, s10, s11, t3);
$display("t4=%x t5=%x t6=%x", t4, t5, t6);
endfunction
reg [31:0] regs [0:30];
assign rs1_o = rs1_i == 0 ? 0 : regs[rs1_i - 1];
assign rs2_o = rs2_i == 0 ? 0 : regs[rs2_i - 1];
integer i;
always @(posedge clk_i) begin
if (rst_i) begin
for (i = 0; i < 31; ++i)
regs[i] <= 0;
end else begin
if (rd_write_i && rd_i > 0) begin
regs[rd_i - 1] <= write_value_i;
end
end
end
endmodule
+68
View File
@@ -0,0 +1,68 @@
`include "rvx0_defs.svh"
// Top-level instruction decoder module
module rvx0_decoder (
input wire [31:0] instruction_i,
output logic write_pc_o,
output logic write_mem_o,
output logic conditional_o,
output logic instruction_fault_o,
output wire [31:0] imm_o,
output wire v1src_o,
output wire [1:0] v2src_o,
output wire [1:0] vdsrc_o,
output wire [3:0] aluop_o,
output wire [2:0] funct3_o,
output wire [6:0] funct7_o,
output wire [4:0] rs1_o,
output wire [4:0] rs2_o,
output wire [4:0] rd_o
);
wire [6:0] opcode;
wire signed [11:0] imm_i_type;
wire signed [11:0] imm_s_type;
wire signed [12:0] imm_b_type;
wire [19:0] imm_u_type;
wire signed [20:0] imm_j_type;
assign opcode = instruction_i[6:0];
assign funct3_o = instruction_i[14:12];
assign funct7_o = instruction_i[31:25];
assign imm_i_type = instruction_i[31:20];
assign imm_s_type = { instruction_i[31:25], instruction_i[11:7] };
assign imm_b_type = { instruction_i[31], instruction_i[7], instruction_i[30:25], instruction_i[11:8], 1'b0 };
assign imm_u_type = instruction_i[31:12];
assign imm_j_type = { instruction_i[31], instruction_i[19:12], instruction_i[20], instruction_i[30:21], 1'b0 };
assign rs1_o = instruction_i[19:15];
assign rs2_o = instruction_i[24:20];
assign rd_o = instruction_i[11:7];
rvx0_decoder_i decoder_i (
.instruction_i (instruction_i),
.opcode (opcode),
.funct3 (funct3_o),
.imm_i_type (imm_i_type),
.imm_s_type (imm_s_type),
.imm_b_type (imm_b_type),
.imm_u_type (imm_u_type),
.imm_j_type (imm_j_type),
.write_pc_o (write_pc_o),
.write_mem_o (write_mem_o),
.conditional_o (conditional_o),
.instruction_fault_o (instruction_fault_o),
.imm_o (imm_o),
.v1src_o (v1src_o),
.v2src_o (v2src_o),
.vdsrc_o (vdsrc_o),
.aluop_o (aluop_o)
);
endmodule
+116
View File
@@ -0,0 +1,116 @@
// Base rv32i instruction set decoder
module rvx0_decoder_i (
input wire [31:0] instruction_i,
input wire [6:0] opcode,
input wire [2:0] funct3,
input wire signed [11:0] imm_i_type,
input wire signed [11:0] imm_s_type,
input wire signed [12:0] imm_b_type,
input wire [19:0] imm_u_type,
input wire signed [20:0] imm_j_type,
output logic write_pc_o,
output logic write_mem_o,
output logic conditional_o,
output logic instruction_fault_o,
output wire [31:0] imm_o,
output wire v1src_o,
output wire [1:0] v2src_o,
output wire [1:0] vdsrc_o,
output wire [3:0] aluop_o
);
always_comb begin
write_pc_o = 0;
write_mem_o = 0;
conditional_o = 0;
instruction_fault_o = 0;
aluop_o = 0;
case (opcode)
7'b0110111: begin // lui
v1src_o = V1SRC_ZERO;
v2src_o = V2SRC_ZERO;
vdsrc_o = RDSRC_ALU;
imm_o = { imm_u_type, 12'b0 };
end
7'b0010111: begin // auipc
v1src_o = V1SRC_ZERO;
v2src_o = V2SRC_PC;
vdsrc_o = RDSRC_ALU;
imm_o = { imm_u_type, 12'b0 };
end
7'b1101111: begin // jal
v1src_o = V1SRC_ZERO;
v2src_o = V2SRC_PC;
vdsrc_o = RDSRC_PC;
write_pc_o = 1;
imm_o = 32'(signed'(imm_j_type));
end
7'b1100111: begin // jalr
v1src_o = V1SRC_RS1;
v2src_o = V2SRC_ZERO;
vdsrc_o = RDSRC_PC;
write_pc_o = 1;
imm_o = 32'(signed'(imm_i_type));
end
7'b1100011: begin // b.???
v1src_o = V1SRC_ZERO;
v2src_o = V2SRC_PC;
vdsrc_o = RDSRC_NONE;
write_pc_o = 1;
conditional_o = 1;
imm_o = 32'(signed'(imm_b_type));
end
7'b0000011: begin // load
v1src_o = V1SRC_RS1;
v2src_o = V2SRC_ZERO;
vdsrc_o = RDSRC_MEM;
imm_o = 32'(signed'(imm_i_type));
end
7'b0100011: begin // store
v1src_o = V1SRC_RS1;
v2src_o = V2SRC_ZERO;
vdsrc_o = RDSRC_NONE;
write_mem_o = 1;
imm_o = 32'(signed'(imm_s_type));
end
7'b0010011: begin // alu imm
v1src_o = V1SRC_RS1;
v2src_o = V2SRC_ZERO;
vdsrc_o = RDSRC_ALU;
case (funct3)
3'b001: begin
aluop_o = ALUOP_SLL;
imm_o = { 27'b0, instruction_i[24:20] };
end
3'b101: begin
aluop_o = { instruction_i[30], funct3 };
imm_o = { 27'b0, instruction_i[24:20] };
end
default: begin
aluop_o = { 1'b0, funct3 };
imm_o = 32'(signed'(imm_i_type));
end
endcase
end
7'b0110011: begin
v1src_o = V1SRC_RS1;
v2src_o = V2SRC_RS2;
vdsrc_o = RDSRC_ALU;
aluop_o = { instruction_i[30], funct3 };
imm_o = 0;
end
default: begin
v1src_o = V1SRC_ZERO;
v2src_o = V2SRC_ZERO;
vdsrc_o = RDSRC_NONE;
instruction_fault_o = 1;
imm_o = 0;
end
endcase
end
endmodule
+122
View File
@@ -0,0 +1,122 @@
/* verilator lint_off PINMISSING */
`include "rvx0_defs.svh"
module rvx0_decoder_tb ();
reg [31:0] instruction;
rvx0_decoder dut (
.instruction_i (instruction)
);
initial begin
// lui sp, 0x12345
instruction = 'h12345137;
#1
assert(dut.imm_o == 'h12345000);
assert(dut.write_pc_o == 0);
assert(dut.v1src_o == V1SRC_ZERO);
assert(dut.v2src_o == V2SRC_ZERO);
assert(dut.vdsrc_o == RDSRC_ALU);
assert(dut.rd_o == 2);
assert(~dut.instruction_fault_o);
// auipc ra, 0xFEEF1
#1
instruction = 'hFEEF1097;
#1
assert(dut.imm_o == 'hFEEF1000);
assert(dut.write_pc_o == 0);
assert(dut.v1src_o == V1SRC_ZERO);
assert(dut.v2src_o == V2SRC_PC);
assert(dut.vdsrc_o == RDSRC_ALU);
assert(dut.rd_o == 1);
assert(~dut.instruction_fault_o);
// j . -> jal zero, 0
#1
instruction = 'h0000006f;
#1
assert(dut.imm_o == 0);
assert(dut.write_pc_o == 1);
assert(dut.v1src_o == V1SRC_ZERO);
assert(dut.v2src_o == V2SRC_PC);
assert(dut.vdsrc_o == RDSRC_PC);
assert(dut.rd_o == 0);
assert(~dut.instruction_fault_o);
// jalr x2, x3, 0x123
#1
instruction = 'hEDD18167;
#1
assert(dut.imm_o == -'h123);
assert(dut.write_pc_o == 1);
assert(dut.v1src_o == V1SRC_RS1);
assert(dut.v2src_o == V2SRC_ZERO);
assert(dut.vdsrc_o == RDSRC_PC);
assert(dut.rd_o == 2);
assert(dut.rs1_o == 3);
assert(~dut.instruction_fault_o);
// bne sp, gp, 0x24
#1
instruction = 'h02311263;
#1
assert(dut.imm_o == 'h24);
assert(dut.write_pc_o == 1);
assert(dut.conditional_o == 1);
assert(dut.v1src_o == V1SRC_ZERO);
assert(dut.v2src_o == V2SRC_PC);
assert(dut.vdsrc_o == RDSRC_NONE);
assert(dut.rs1_o == 2);
assert(dut.rs2_o == 3);
assert(dut.funct3_o == 'b001);
assert(~dut.instruction_fault_o);
// lw t0, 0x10(t1)
#1
instruction = 'h01032283;
#1
assert(dut.imm_o == 'h10);
assert(dut.write_pc_o == 0);
assert(dut.write_mem_o == 0);
assert(dut.v1src_o == V1SRC_RS1);
assert(dut.v2src_o == V2SRC_ZERO);
assert(dut.vdsrc_o == RDSRC_MEM);
assert(dut.rs1_o == 6);
assert(dut.rd_o == 5);
assert(dut.funct3_o == 'b010);
assert(~dut.instruction_fault_o);
// sh t0, -0x10(t1)
#1
instruction = 'hFE531823;
#1
assert(dut.imm_o == -'h10);
assert(dut.write_pc_o == 0);
assert(dut.write_mem_o == 1);
assert(dut.v1src_o == V1SRC_RS1);
assert(dut.v2src_o == V2SRC_ZERO);
assert(dut.vdsrc_o == RDSRC_NONE);
assert(dut.rs1_o == 6);
assert(dut.rs2_o == 5);
assert(dut.funct3_o == 'b001);
assert(~dut.instruction_fault_o);
// xori t0, t2, 0xC
#1
instruction = 'h00C3C293;
#1
assert(dut.imm_o == 'hC);
assert(dut.write_pc_o == 0);
assert(dut.write_mem_o == 0);
assert(dut.v1src_o == V1SRC_RS1);
assert(dut.v2src_o == V2SRC_ZERO);
assert(dut.vdsrc_o == RDSRC_ALU);
assert(dut.rd_o == 5);
assert(dut.rs1_o == 7);
assert(dut.funct3_o == 'b100);
assert(dut.aluop_o == 'b0100);
assert(~dut.instruction_fault_o);
end
endmodule
+25
View File
@@ -0,0 +1,25 @@
/* verilator lint_off UNOPTFLAT */
interface rvx0_apb ();
logic pclk;
logic prstn;
logic [31:0] paddr;
logic pwrite;
logic penable;
logic [31:0] pwdata;
logic psel;
logic pready;
logic [31:0] prdata;
logic pslverr;
modport master (
input pready, prdata, pslverr,
output pclk, prstn, paddr, penable, pwrite, pwdata
);
modport slave (
input pclk, prstn, paddr, pwrite, penable, pwdata, psel,
output pready, prdata, pslverr
);
endinterface
+20
View File
@@ -0,0 +1,20 @@
module rvx0_apb_debug (
rvx0_apb.slave apb_slave
);
integer fd;
initial begin
fd = $fopen("debug.log", "w");
end
always @(posedge apb_slave.pclk) begin
if (apb_slave.prstn && apb_slave.penable) begin
if (apb_slave.pwrite) begin
$fwrite(fd, "%c", apb_slave.pwdata[7:0]);
$fflush(fd);
end
apb_slave.pready <= 1;
end
end
endmodule
+98
View File
@@ -0,0 +1,98 @@
module rvx0_busmux (
// Uplink to core
rvx0_apb.slave apb_slave,
// Downlinks to peripherals/memory
rvx0_apb.master apb0_master,
rvx0_apb.master apb1_master,
rvx0_apb.master apb2_master,
rvx0_apb.master apb3_master
);
// ROM
const logic [31:0] APB0_BASE = 'h00001000;
const logic [31:0] APB0_SIZE = 'h00020000;
// RAM
const logic [31:0] APB1_BASE = 'h20000000;
const logic [31:0] APB1_SIZE = 'h00001000;
// Debug
const logic [31:0] APB2_BASE = 'h10000000;
const logic [31:0] APB2_SIZE = 'h00001000;
// Forward pclk/prstn
assign apb0_master.pclk = apb_slave.pclk;
assign apb1_master.pclk = apb_slave.pclk;
assign apb2_master.pclk = apb_slave.pclk;
assign apb3_master.pclk = apb_slave.pclk;
assign apb0_master.prstn = apb_slave.prstn;
assign apb1_master.prstn = apb_slave.prstn;
assign apb2_master.prstn = apb_slave.prstn;
assign apb3_master.prstn = apb_slave.prstn;
always_comb begin
apb0_master.penable = 0;
apb1_master.penable = 0;
apb2_master.penable = 0;
apb3_master.penable = 0;
apb0_master.pwrite = 0;
apb1_master.pwrite = 0;
apb2_master.pwrite = 0;
apb3_master.pwrite = 0;
apb0_master.pwdata = 0;
apb1_master.pwdata = 0;
apb2_master.pwdata = 0;
apb3_master.pwdata = 0;
apb0_master.paddr = 0;
apb1_master.paddr = 0;
apb2_master.paddr = 0;
apb3_master.paddr = 0;
apb_slave.pready = 0;
apb_slave.pslverr = 0;
apb_slave.prdata = 0;
if (apb_slave.prstn && apb_slave.penable) begin
if (apb_slave.paddr >= APB0_BASE && apb_slave.paddr < APB0_BASE + APB0_SIZE) begin
// Connect to apb0
apb0_master.paddr = apb_slave.paddr - APB0_BASE;
apb0_master.pwdata = apb_slave.pwdata;
apb0_master.pwrite = apb_slave.pwrite;
apb0_master.penable = apb_slave.penable;
apb_slave.pready = apb0_master.pready && apb_slave.penable;
apb_slave.pslverr = apb0_master.pslverr;
apb_slave.prdata = apb0_master.prdata;
end else if (apb_slave.paddr >= APB1_BASE && apb_slave.paddr < APB1_BASE + APB1_SIZE) begin
// Connect to apb1
apb1_master.paddr = apb_slave.paddr - APB1_BASE;
apb1_master.pwdata = apb_slave.pwdata;
apb1_master.pwrite = apb_slave.pwrite;
apb1_master.penable = apb_slave.penable;
apb_slave.pready = apb1_master.pready && apb_slave.penable;
apb_slave.pslverr = apb1_master.pslverr;
apb_slave.prdata = apb1_master.prdata;
end else if (apb_slave.paddr >= APB2_BASE && apb_slave.paddr < APB2_BASE + APB2_SIZE) begin
// Connect to apb2
apb2_master.paddr = apb_slave.paddr - APB2_BASE;
apb2_master.pwdata = apb_slave.pwdata;
apb2_master.pwrite = apb_slave.pwrite;
apb2_master.penable = apb_slave.penable;
apb_slave.pready = apb2_master.pready && apb_slave.penable;
apb_slave.pslverr = apb2_master.pslverr;
apb_slave.prdata = apb2_master.prdata;
end else begin
// Raise bus error
apb_slave.pslverr = 1;
end
end else begin
apb_slave.pslverr = 0;
end
end
endmodule
+61
View File
@@ -0,0 +1,61 @@
`ifndef __RVX0_DEFS_SVH
`define __RVX0_DEFS_SVH
`define RVX0_DEBUGGING 1
const logic [1:0] RDSRC_ALU = 0;
const logic [1:0] RDSRC_MEM = 1;
const logic [1:0] RDSRC_PC = 2;
const logic [1:0] RDSRC_NONE = 3;
const logic V1SRC_ZERO = 0;
const logic V1SRC_RS1 = 1;
const logic [1:0] V2SRC_ZERO = 0;
const logic [1:0] V2SRC_RS2 = 1;
const logic [1:0] V2SRC_PC = 2;
const logic [3:0] ALUOP_ADD = 4'b0000;
const logic [3:0] ALUOP_SLL = 4'b0001;
const logic [3:0] ALUOP_SLT = 4'b0010;
const logic [3:0] ALUOP_SLTU = 4'b0011;
const logic [3:0] ALUOP_XOR = 4'b0100;
const logic [3:0] ALUOP_SRL = 4'b0101;
const logic [3:0] ALUOP_OR = 4'b0110;
const logic [3:0] ALUOP_AND = 4'b0111;
const logic [3:0] ALUOP_SUB = 4'b1000;
const logic [3:0] ALUOP_SRA = 4'b1101;
const logic [2:0] COND_EQ = 3'b000;
const logic [2:0] COND_NE = 3'b001;
const logic [2:0] COND_LT = 3'b100;
const logic [2:0] COND_GE = 3'b101;
const logic [2:0] COND_LTU = 3'b110;
const logic [2:0] COND_GEU = 3'b111;
const logic [1:0] CSTATE_FETCH = 0;
const logic [1:0] CSTATE_READ = 1;
const logic [1:0] CSTATE_EXEC = 2;
const logic [1:0] CSTATE_WRITE = 3;
const logic [31:0] MISA_MXLEN_32 = 1 << 30;
const logic [31:0] MCAUSE_INSTRUCTION_ADDRESS_MISALIGN = 0;
const logic [31:0] MCAUSE_INSTRUCTION_ACCESS_FAULT = 1;
const logic [31:0] MCAUSE_ILLEGAL_INSTRUCTION = 2;
const logic [31:0] MCAUSE_BREAKPOINT = 3;
const logic [31:0] MCAUSE_LOAD_ADDRESS_MISALIGNED = 4;
const logic [31:0] MCAUSE_LOAD_ACCESS_FAULT = 5;
const logic [31:0] MCAUSE_STORE_ADDRESS_MISALIGNED = 6;
const logic [31:0] MCAUSE_STORE_ACCESS_FAULT = 7;
const logic [31:0] MCAUSE_ECALL_UMODE = 8;
const logic [31:0] MCAUSE_ECALL_SMODE = 9;
const logic [31:0] MCAUSE_ECALL_MMODE = 11;
const logic [31:0] MCAUSE_INSTRUCTION_PAGE_FAULT = 12;
const logic [31:0] MCAUSE_LOAD_PAGE_FAULT = 13;
const logic [31:0] MCAUSE_STORE_PAGE_FAULT = 15;
const logic [31:0] MCAUSE_INTR_EXT_MMODE = 11;
const logic [31:0] MCAUSE_INTERRUPT = 1 << 31;
`endif // __RVX0_DEFS_SVH
+30
View File
@@ -0,0 +1,30 @@
module rvx0_simple_ram (
rvx0_apb.slave apb_slave
);
reg [31:0] words [0:1023];
integer i;
always @(posedge apb_slave.pclk) begin
if (apb_slave.prstn) begin
if (apb_slave.penable) begin
if (apb_slave.pwrite) begin
words[apb_slave.paddr[11:2]] <= apb_slave.pwdata;
$display("RAM write [%x] <= %x", apb_slave.paddr, apb_slave.pwdata);
end else begin
apb_slave.prdata <= words[apb_slave.paddr[11:2]];
$display("RAM read %x <= [%x]", words[apb_slave.paddr[11:2]], apb_slave.paddr);
end
apb_slave.pready <= 1;
end else begin
apb_slave.pready <= 0;
end
end else begin
// for (i = 0; i < 1024; ++i)
// words[i] <= 0;
words[0] <= 'h11223344;
end
end
endmodule
+15
View File
@@ -0,0 +1,15 @@
module rvx0_simple_rom (
rvx0_apb.slave apb_slave
);
// 1 page
reg [31:0] words [0:32768 - 1];
always_comb begin
apb_slave.pready = apb_slave.penable;
apb_slave.prdata = words[apb_slave.paddr[16:2]];
end
initial begin
$readmemh("obj_dir/apb0_memory.hex", words);
end
endmodule
+67
View File
@@ -0,0 +1,67 @@
`include "rvx0_defs.svh"
/* verilator lint_off PINMISSING */
module rvx0_soc_top ();
logic clk;
logic rst;
logic intr_ext;
rvx0_apb apb ();
rvx0_apb apb0 ();
rvx0_apb apb1 ();
rvx0_apb apb2 ();
rvx0_apb apb3 ();
rvx0_core #(
.RESET_VECTOR ('h00001000)
) core (
.clk_i (clk),
.rst_i (rst),
.intr_ext_i (intr_ext),
.apb_master (apb)
);
rvx0_simple_rom rom (
.apb_slave (apb0)
);
rvx0_simple_ram ram (
.apb_slave (apb1)
);
rvx0_apb_debug debug (
.apb_slave (apb2)
);
rvx0_busmux busmux (
.apb_slave (apb),
.apb0_master (apb0),
.apb1_master (apb1),
.apb2_master (apb2),
.apb3_master (apb3)
);
always #10 clk <= ~clk;
assign apb.pclk = clk;
assign apb.prstn = ~rst;
initial begin
#0
intr_ext = 0;
rst = 1;
#20
rst = 0;
// #50000
// // Dump state
// $display("Finish with pc=%x", core.pc_value);
// core.regfile.dump_state;
// $finish;
end
endmodule
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
import sys
import struct
if __name__ == "__main__":
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} SOURCE.BIN OUTPUT.HEX", file=sys.stderr)
sys.exit(2)
data = None
with open(sys.argv[1], "rb") as fp:
data = fp.read()
with open(sys.argv[2], "w") as fp:
for i in range(0, len(data), 4):
word = data[i:i + 4]
word = struct.unpack("<I", word)[0]
fp.write(f"{word:x}\n")