Compare commits

..

2 Commits

Author SHA1 Message Date
3a3c951409 Added timer, still wip 2026-02-25 20:54:12 +01:00
f2f9644830 Added qerv files 2026-02-25 20:52:07 +01:00
21 changed files with 2480 additions and 200 deletions

View File

@@ -22,8 +22,8 @@ package = tqg144
speedgrade = -2
toplevel = top_generic
xst_opts = -vlgincdir rtl/util
files_verilog = rtl/util/conv.vh
rtl/toplevel/top_generic.v
files_verilog = rtl/toplevel/top_generic.v
rtl/util/conv.vh
rtl/core/nco_q15.v
rtl/core/sigmadelta_sampler.v
rtl/core/sigmadelta_rcmodel_q15.v
@@ -31,8 +31,14 @@ files_verilog = rtl/util/conv.vh
rtl/core/mul_const.v
rtl/core/lpf_iir_q15_k.v
rtl/core/decimate_by_r_q15.v
rtl/core/mcu_peripherals.v
rtl/core/mcu.v
rtl/core/mem_jtag_writable.v
# Arch
rtl/arch/spartan-6/lvds_comparator.v
rtl/arch/spartan-6/clk_gen.v
rtl/arch/spartan-6/jtag_if.v
# SERV
rtl/serv/serv_aligner.v
rtl/serv/serv_alu.v
rtl/serv/serv_bufreg.v
@@ -47,25 +53,104 @@ files_verilog = rtl/util/conv.vh
rtl/serv/serv_rf_if.v
rtl/serv/serv_rf_ram_if.v
rtl/serv/serv_rf_ram.v
rtl/serv/serv_rf_top.v
rtl/serv/serv_state.v
rtl/serv/serv_rf_top.v
rtl/serv/serv_synth_wrapper.v
rtl/serv/serv_top.v
# QERV
# rtl/qerv/serv_rf_top.v
# rtl/qerv/serv_synth_wrapper.v
# rtl/qerv/serv_top.v
# rtl/qerv/qerv_immdec.v
# Servile
rtl/serv/servile_arbiter.v
rtl/serv/servile_mux.v
rtl/serv/servile_rf_mem_if.v
rtl/serv/servile.v
rtl/serv/serving_ram.v
rtl/serv/serving.v
rtl/arch/spartan-6/jtag_if.v
# rtl/qerv/servile_arbiter.v
# rtl/qerv/servile_mux.v
# rtl/qerv/servile_rf_mem_if.v
# rtl/qerv/servile.v
# WB
rtl/wb/wb_gpio.v
rtl/wb/wb_gpio_banks.v
rtl/wb/wb_mux.v
rtl/wb/jtag_wb_bridge.v
rtl/core/mcu.v
rtl/wb/wb_timer.v
files_con = boards/mimas_v1/constraints.ucf
files_other = rtl/util/rc_alpha_q15.vh
rtl/util/clog2.vh
sw/blinky/blinky.hex
sw/sweep/sweep.hex
[target.synth_sim]
toolchain = iverilog
runtime = all
toplevel = tb_top_generic
ivl_opts = -Irtl/util
files_verilog = rtl/toplevel/top_generic.v
rtl/util/conv.vh
rtl/core/nco_q15.v
rtl/core/sigmadelta_sampler.v
rtl/core/sigmadelta_rcmodel_q15.v
rtl/core/sigmadelta_input_q15.v
rtl/core/mul_const.v
rtl/core/lpf_iir_q15_k.v
rtl/core/decimate_by_r_q15.v
rtl/core/mcu_peripherals.v
rtl/core/mcu.v
rtl/core/mem_jtag_writable.v
# Arch
rtl/core/lvds_comparator.v
sim/overrides/clk_gen.v
rtl/core/jtag_if.v
# SERV
rtl/serv/serv_aligner.v
rtl/serv/serv_alu.v
rtl/serv/serv_bufreg.v
rtl/serv/serv_bufreg2.v
rtl/serv/serv_compdec.v
rtl/serv/serv_csr.v
rtl/serv/serv_ctrl.v
rtl/serv/serv_debug.v
rtl/serv/serv_decode.v
rtl/serv/serv_immdec.v
rtl/serv/serv_mem_if.v
rtl/serv/serv_rf_if.v
rtl/serv/serv_rf_ram_if.v
rtl/serv/serv_rf_ram.v
rtl/serv/serv_state.v
rtl/serv/serv_rf_top.v
rtl/serv/serv_synth_wrapper.v
rtl/serv/serv_top.v
# QERV
# rtl/qerv/serv_rf_top.v
# rtl/qerv/serv_synth_wrapper.v
# rtl/qerv/serv_top.v
# rtl/qerv/qerv_immdec.v
# Servile
rtl/serv/servile_arbiter.v
rtl/serv/servile_mux.v
rtl/serv/servile_rf_mem_if.v
rtl/serv/servile.v
# rtl/qerv/servile_arbiter.v
# rtl/qerv/servile_mux.v
# rtl/qerv/servile_rf_mem_if.v
# rtl/qerv/servile.v
# WB
rtl/wb/wb_gpio.v
rtl/wb/wb_gpio_banks.v
rtl/wb/wb_mux.v
rtl/wb/jtag_wb_bridge.v
rtl/wb/wb_timer.v
sim/tb/tb_top_generic.v
files_con = boards/mimas_v1/constraints.ucf
files_other = rtl/util/rc_alpha_q15.vh
rtl/util/clog2.vh
rtl/util/conv.vh
sw/sweep/sweep.hex
[target.jtag]
toolchain = ISE
@@ -93,6 +178,13 @@ files_verilog = sim/tb/tb_svf.v
rtl/core/cdc_strobed.v
files_other = sim/other/test.svf
[target.tb_wb_timer]
toolchain = iverilog
runtime = all
toplevel = tb_wb_timer
files_verilog = sim/tb/tb_wb_timer.v
rtl/wb/wb_timer.v
[target.tools]
toolchain = make
output_files = tools/test

View File

@@ -16,7 +16,8 @@ module mcu #(
output wire [31:0] o_GPO_A,
output wire [31:0] o_GPO_B,
output wire [31:0] o_GPO_C,
output wire [31:0] o_GPO_D
output wire [31:0] o_GPO_D,
output wire o_test
);
localparam WITH_CSR = 1;
localparam regs = 32+WITH_CSR*4;
@@ -26,7 +27,8 @@ module mcu #(
wire rst_mem_reason;
wire timer_irq;
assign rst = i_rst | rst_mem_reason;
assign timer_irq = 1'b0;
assign o_test = timer_irq;
// Busses
// CPU->memory
@@ -86,7 +88,7 @@ module mcu #(
) servile (
.i_clk(i_clk),
.i_rst(rst),
.i_timer_irq(timer_irq),
.i_timer_irq(1'b0), //timer_irq),
//Memory interface
.o_wb_mem_adr(wb_mem_adr),
@@ -116,14 +118,14 @@ module mcu #(
);
// WB arbiter combining RF and mem interfaces into 1
// Last 128 bytes are used for registers
// Last 128 bytes are used for registers
servile_rf_mem_if #(
.depth(memsize),
.rf_regs(regs)
) rf_mem_if (
.i_clk (i_clk),
.i_rst (i_rst),
.i_rst (rst),
.i_waddr(rf_waddr),
.i_wdata(rf_wdata),
.i_wen(rf_wen),
@@ -162,111 +164,20 @@ module mcu #(
.o_core_reset(rst_mem_reason)
);
wb_gpio_banks #(
.BASE_ADDR(32'h40000000),
.NUM_BANKS(4)
) gpio (
.i_wb_clk(i_clk),
.i_wb_rst(rst),
.i_wb_dat(wb_ext_dat),
mcu_peripherals peripherals (
.i_clk(i_clk),
.i_rst(rst),
.i_wb_adr(wb_ext_adr),
.i_wb_dat(wb_ext_dat),
.i_wb_sel(wb_ext_sel),
.i_wb_we(wb_ext_we),
.i_wb_stb(wb_ext_stb),
.i_wb_sel(wb_ext_sel),
.o_wb_rdt(wb_ext_rdt),
.o_wb_ack(wb_ext_ack),
// Peripheral IO
.i_gpio(GPI),
.o_gpio(GPO)
.o_gpio(GPO),
.o_timer_irq(timer_irq)
);
endmodule
module memory #(
parameter memfile = "",
parameter depth = 256,
parameter sim = 1'b0,
localparam aw = `CLOG2(depth)
)(
input wire i_clk,
input wire i_rst,
input wire [aw-1:0] i_waddr,
input wire [7:0] i_wdata,
input wire i_wen,
input wire [aw-1:0] i_raddr,
output reg [7:0] o_rdata,
output wire o_core_reset
);
// The actual memory
reg [7:0] mem [0:depth-1];
wire [aw-1:0] mem_adr;
assign mem_adr = (i_wen==1'b1) ? i_waddr :
i_raddr;
// Second port wishbone
wire [31:0] wb_adr;
wire [31:0] wb_dat;
reg [31:0] wb_rdt;
wire [3:0] wb_sel;
wire wb_cyc;
wire wb_we;
wire wb_stb;
reg wb_ack;
reg wb_req_d;
wire cmd_reset;
// Driven by JTAG
jtag_wb_bridge #(
.chain(1),
.byte_aligned(1)
) jtag_wb (
.i_clk(i_clk),
.i_rst(i_rst),
.o_wb_adr(wb_adr),
.o_wb_dat(wb_dat),
.o_wb_sel(wb_sel),
.o_wb_we(wb_we),
.o_wb_cyc(wb_cyc),
.o_wb_stb(wb_stb),
.i_wb_rdt(wb_rdt),
.i_wb_ack(wb_ack),
.o_cmd_reset(cmd_reset)
);
assign o_core_reset = cmd_reset;
// Read/Write
always @(posedge i_clk) begin
if (i_rst) begin
wb_req_d <= 1'b0;
wb_ack <= 1'b0;
wb_rdt <= 32'h00000000;
o_rdata <= 32'h00000000;
end else begin
if (i_wen)
mem[mem_adr] <= i_wdata;
o_rdata <= mem[mem_adr];
wb_req_d <= wb_stb && wb_cyc;
wb_ack <= wb_req_d;
if (wb_we && wb_stb && wb_cyc)
mem[wb_adr[aw-1:0]] <= wb_dat[7:0];
wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]};
end
end
// Preload memory
integer i;
initial begin
if(sim==1'b1) begin
for (i = 0; i < depth; i = i + 1)
mem[i] = 8'h00;
end
if(|memfile) begin
$display("Preloading %m from %s", memfile);
$readmemh(memfile, mem);
end
end
endmodule
endmodule

123
rtl/core/mcu_peripherals.v Normal file
View File

@@ -0,0 +1,123 @@
`timescale 1ns/1ps
module mcu_peripherals (
input wire i_clk,
input wire i_rst,
input wire [31:0] i_wb_adr,
input wire [31:0] i_wb_dat,
input wire [3:0] i_wb_sel,
input wire i_wb_we,
input wire i_wb_stb,
output wire [31:0] o_wb_rdt,
output wire o_wb_ack,
input wire [4*32-1:0] i_gpio,
output wire [4*32-1:0] o_gpio,
output wire o_timer_irq
);
localparam [31:0] GPIO_BASE_ADDR = 32'h4000_0000;
localparam [31:0] GPIO_ADDR_MASK = 32'hFFFF_0000;
localparam [31:0] TIMER_BASE_ADDR = 32'h4001_0000;
localparam [31:0] TIMER_ADDR_MASK = 32'hFFFF_0000;
wire [2*32-1:0] wbs_adr;
wire [2*32-1:0] wbs_dat_w;
wire [2*4-1:0] wbs_sel;
wire [1:0] wbs_we;
wire [1:0] wbs_cyc;
wire [1:0] wbs_stb;
wire [2*3-1:0] wbs_cti;
wire [2*2-1:0] wbs_bte;
wire [2*32-1:0] wbs_dat_r;
wire [1:0] wbs_ack;
wire [31:0] gpio_wbs_adr = wbs_adr[0*32 +: 32];
wire [31:0] gpio_wbs_dat_w = wbs_dat_w[0*32 +: 32];
wire [3:0] gpio_wbs_sel = wbs_sel[0*4 +: 4];
wire gpio_wbs_we = wbs_we[0];
wire gpio_wbs_cyc = wbs_cyc[0];
wire gpio_wbs_stb = wbs_stb[0];
wire [31:0] gpio_wbs_dat_r;
wire gpio_wbs_ack;
wire [31:0] timer_wbs_dat_w = wbs_dat_w[1*32 +: 32];
wire timer_wbs_we = wbs_we[1];
wire timer_wbs_cyc = wbs_cyc[1];
wire timer_wbs_stb = wbs_stb[1];
wire [31:0] timer_wbs_dat_r;
wire timer_wbs_ack;
wb_mux #(
.dw(32),
.aw(32),
.num_slaves(2),
.MATCH_ADDR({TIMER_BASE_ADDR, GPIO_BASE_ADDR}),
.MATCH_MASK({TIMER_ADDR_MASK, GPIO_ADDR_MASK})
) ext_mux (
.wb_clk_i(i_clk),
.wb_rst_i(i_rst),
.wbm_adr_i(i_wb_adr),
.wbm_dat_i(i_wb_dat),
.wbm_sel_i(i_wb_sel),
.wbm_we_i(i_wb_we),
.wbm_cyc_i(i_wb_stb),
.wbm_stb_i(i_wb_stb),
.wbm_cti_i(3'b000),
.wbm_bte_i(2'b00),
.wbm_dat_o(o_wb_rdt),
.wbm_ack_o(o_wb_ack),
.wbm_err_o(),
.wbm_rty_o(),
.wbs_adr_o(wbs_adr),
.wbs_dat_o(wbs_dat_w),
.wbs_sel_o(wbs_sel),
.wbs_we_o(wbs_we),
.wbs_cyc_o(wbs_cyc),
.wbs_stb_o(wbs_stb),
.wbs_cti_o(wbs_cti),
.wbs_bte_o(wbs_bte),
.wbs_dat_i(wbs_dat_r),
.wbs_ack_i(wbs_ack),
.wbs_err_i(2'b00),
.wbs_rty_i(2'b00)
);
wb_gpio_banks #(
.BASE_ADDR(GPIO_BASE_ADDR),
.NUM_BANKS(4)
) gpio (
.i_wb_clk(i_clk),
.i_wb_rst(i_rst),
.i_wb_dat(gpio_wbs_dat_w),
.i_wb_adr(gpio_wbs_adr),
.i_wb_we(gpio_wbs_we),
.i_wb_stb(gpio_wbs_stb & gpio_wbs_cyc),
.i_wb_sel(gpio_wbs_sel),
.o_wb_rdt(gpio_wbs_dat_r),
.o_wb_ack(gpio_wbs_ack),
.i_gpio(i_gpio),
.o_gpio(o_gpio)
);
assign wbs_dat_r[0*32 +: 32] = gpio_wbs_dat_r;
assign wbs_ack[0] = gpio_wbs_ack;
wb_countdown_timer timer (
.i_clk(i_clk),
.i_rst(i_rst),
.o_irq(o_timer_irq),
.i_wb_dat(timer_wbs_dat_w),
.o_wb_dat(timer_wbs_dat_r),
.i_wb_we(timer_wbs_we),
.i_wb_cyc(timer_wbs_cyc),
.i_wb_stb(timer_wbs_stb),
.o_wb_ack(timer_wbs_ack)
);
assign wbs_dat_r[1*32 +: 32] = timer_wbs_dat_r;
assign wbs_ack[1] = timer_wbs_ack;
endmodule

View File

@@ -0,0 +1,91 @@
`timescale 1ns/1ps
module memory #(
parameter memfile = "",
parameter depth = 256,
parameter sim = 1'b0,
parameter aw = `CLOG2(depth)
)(
input wire i_clk,
input wire i_rst,
input wire [aw-1:0] i_waddr,
input wire [7:0] i_wdata,
input wire i_wen,
input wire [aw-1:0] i_raddr,
output reg [7:0] o_rdata,
output wire o_core_reset
);
// The actual memory
reg [7:0] mem [0:depth-1];
wire [aw-1:0] mem_adr;
assign mem_adr = (i_wen==1'b1) ? i_waddr :
i_raddr;
// Second port wishbone
wire [31:0] wb_adr;
wire [31:0] wb_dat;
reg [31:0] wb_rdt;
wire [3:0] wb_sel;
wire wb_cyc;
wire wb_we;
wire wb_stb;
reg wb_ack;
reg wb_req_d;
wire cmd_reset;
// Driven by JTAG
jtag_wb_bridge #(
.chain(1),
.byte_aligned(1)
) jtag_wb (
.i_clk(i_clk),
.i_rst(i_rst),
.o_wb_adr(wb_adr),
.o_wb_dat(wb_dat),
.o_wb_sel(wb_sel),
.o_wb_we(wb_we),
.o_wb_cyc(wb_cyc),
.o_wb_stb(wb_stb),
.i_wb_rdt(wb_rdt),
.i_wb_ack(wb_ack),
.o_cmd_reset(cmd_reset)
);
assign o_core_reset = cmd_reset;
// Read/Write
always @(posedge i_clk) begin
if (i_rst) begin
wb_req_d <= 1'b0;
wb_ack <= 1'b0;
wb_rdt <= 32'h00000000;
o_rdata <= 32'h00000000;
end else begin
if (i_wen)
mem[mem_adr] <= i_wdata;
o_rdata <= mem[mem_adr];
wb_req_d <= wb_stb && wb_cyc;
wb_ack <= wb_req_d;
if (wb_we && wb_stb && wb_cyc)
mem[wb_adr[aw-1:0]] <= wb_dat[7:0];
wb_rdt <= {24'h000000, mem[wb_adr[aw-1:0]]};
end
end
// Preload memory
integer i;
initial begin
if(sim==1'b1) begin
for (i = 0; i < depth; i = i + 1)
mem[i] = 8'h00;
end
if(|memfile) begin
$display("Preloading %m from %s", memfile);
$readmemh(memfile, mem);
end
end
endmodule

View File

@@ -1,77 +0,0 @@
`timescale 1ns/1ps
module soclet #(
parameter memfile = "",
parameter memsize = 8192,
parameter sim = 1'b0
)(
input wire i_clk,
input wire i_rst,
input wire [31:0] i_GPI_A,
input wire [31:0] i_GPI_B,
input wire [31:0] i_GPI_C,
input wire [31:0] i_GPI_D,
output wire [31:0] o_GPO_A,
output wire [31:0] o_GPO_B,
output wire [31:0] o_GPO_C,
output wire [31:0] o_GPO_D
);
wire [31:0] wb_adr;
wire [31:0] wb_dat;
wire [31:0] wb_rdt;
wire [3:0] wb_sel;
wire wb_we;
wire wb_stb;
wire wb_ack;
wire [4*32-1:0] GPO;
wire [4*32-1:0] GPI;
assign o_GPO_A = GPO[32*1-1:32*0];
assign o_GPO_B = GPO[32*2-1:32*1];
assign o_GPO_C = GPO[32*3-1:32*2];
assign o_GPO_D = GPO[32*4-1:32*3];
assign GPI[32*1-1:32*0] = i_GPI_A;
assign GPI[32*2-1:32*1] = i_GPI_B;
assign GPI[32*3-1:32*2] = i_GPI_C;
assign GPI[32*4-1:32*3] = i_GPI_D;
serving #(
.memfile(memfile),
.memsize(memsize),
.sim(sim),
.RESET_STRATEGY("MINI"),
.WITH_CSR(1)
) serv (
.i_clk(i_clk),
.i_rst(i_rst),
.i_timer_irq(1'b0),
.i_wb_rdt(wb_rdt),
.i_wb_ack(wb_ack),
.o_wb_adr(wb_adr),
.o_wb_dat(wb_dat),
.o_wb_sel(wb_sel),
.o_wb_we(wb_we),
.o_wb_stb(wb_stb)
);
wb_gpio_banks #(
.BASE_ADDR(32'h40000000),
.NUM_BANKS(4)
) gpio (
.i_wb_clk(i_clk),
.i_wb_rst(i_rst),
.i_wb_dat(wb_dat),
.i_wb_adr(wb_adr),
.i_wb_we(wb_we),
.i_wb_stb(wb_stb),
.i_wb_sel(wb_sel),
.i_gpio(GPI),
.o_wb_rdt(wb_rdt),
.o_wb_ack(wb_ack),
.o_gpio(GPO)
);
endmodule

7
rtl/qerv/LICENSE Normal file
View File

@@ -0,0 +1,7 @@
ISC License
Copyright 2019, Olof Kindgren
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

152
rtl/qerv/qerv_immdec.v Normal file
View File

@@ -0,0 +1,152 @@
// SPDX-License-Identifier: ISC
`default_nettype none
module qerv_immdec
(
input wire i_clk,
//State
input wire i_cnt_en,
input wire i_cnt_done,
//Control
input wire [3:0] i_immdec_en,
input wire i_csr_imm_en,
input wire [3:0] i_ctrl,
output wire [4:0] o_rd_addr,
output wire [4:0] o_rs1_addr,
output wire [4:0] o_rs2_addr,
//Data
output wire [3:0] o_csr_imm,
output wire [3:0] o_imm,
//External
input wire i_wb_en,
input wire [31:7] i_wb_rdt);
reg [4:0] rd_addr;
reg [4:0] rs1_addr;
reg [4:0] rs2_addr;
reg i31;
reg i30;
reg i29;
reg i28;
reg i27;
reg i26;
reg i25;
reg i24;
reg i23;
reg i22;
reg i21;
reg i20;
reg i19;
reg i18;
reg i17;
reg i16;
reg i15;
reg i14;
reg i13;
reg i12;
reg i11;
reg i10;
reg i9;
reg i8;
reg i7;
reg i7_2;
reg i20_2;
wire signbit = i31 & !i_csr_imm_en;
assign o_csr_imm[3] = i18;
assign o_csr_imm[2] = i17;
assign o_csr_imm[1] = i16;
assign o_csr_imm[0] = i15;
assign o_rd_addr = rd_addr;
assign o_rs1_addr = rs1_addr;
assign o_rs2_addr = rs2_addr;
always @(posedge i_clk) begin
if (i_wb_en) begin
//Common
i31 <= i_wb_rdt[31];
//Bit lane 3
i19 <= i_wb_rdt[19];
i15 <= i_wb_rdt[15];
i20 <= i_wb_rdt[20];
i7 <= i_wb_rdt[7];
i27 <= i_wb_rdt[27];
i23 <= i_wb_rdt[23];
i10 <= i_wb_rdt[10];
//Bit lane 2
i22 <= i_wb_rdt[22];
i9 <= i_wb_rdt[ 9];
i26 <= i_wb_rdt[26];
i30 <= i_wb_rdt[30];
i14 <= i_wb_rdt[14];
i18 <= i_wb_rdt[18];
//Bit lane 1
i21 <= i_wb_rdt[21];
i8 <= i_wb_rdt[ 8];
i25 <= i_wb_rdt[25];
i29 <= i_wb_rdt[29];
i13 <= i_wb_rdt[13];
i17 <= i_wb_rdt[17];
//Bit lane 0
i11 <= i_wb_rdt[11];
i7_2 <= i_wb_rdt[7 ];
i20_2 <= i_wb_rdt[20];
i24 <= i_wb_rdt[24];
i28 <= i_wb_rdt[28];
i12 <= i_wb_rdt[12];
i16 <= i_wb_rdt[16];
rd_addr <= i_wb_rdt[11:7];
rs1_addr <= i_wb_rdt[19:15];
rs2_addr <= i_wb_rdt[24:20];
end
if (i_cnt_en) begin
//Bit lane 3
i10 <= i27;
i23 <= i27;
i27 <= i_ctrl[2] ? i7 : i_ctrl[1] ? signbit : i20;
i7 <= signbit;
i20 <= i15;
i15 <= i19;
i19 <= i_ctrl[3] ? signbit : i23;
//Bit lane 2
i22 <= i26;
i9 <= i26;
i26 <= i30;
i30 <= (i_ctrl[1] | i_ctrl[2]) ? signbit : i14;
i14 <= i18;
i18 <= i_ctrl[3] ? signbit : i22;
//Bit lane 1
i21 <= i25;
i8 <= i25;
i25 <= i29;
i29 <= (i_ctrl[1] | i_ctrl[2]) ? signbit : i13;
i13 <= i17;
i17 <= i_ctrl[3] ? signbit : i21;
//Bit lane 0
i7_2 <= i11;
i11 <= i28;
i20_2 <= i24;
i24 <= i28;
i28 <= (i_ctrl[1] | i_ctrl[2]) ? signbit : i12;
i12 <= i16;
i16 <= i_ctrl[3] ? signbit : i20_2;
end
end
assign o_imm[3] = (i_cnt_done ? signbit : (i_ctrl[0] ? i10 : i23));
assign o_imm[2] = i_ctrl[0] ? i9 : i22;
assign o_imm[1] = i_ctrl[0] ? i8 : i21;
assign o_imm[0] = i_ctrl[0] ? i7_2 : i20_2;
endmodule

222
rtl/qerv/serv_rf_top.v Normal file
View File

@@ -0,0 +1,222 @@
`default_nettype none
`include "../util/clog2.vh"
module qerv_rf_top
#(parameter RESET_PC = 32'd0,
/* COMPRESSED=1: Enable the compressed decoder and allowed misaligned jump of pc
COMPRESSED=0: Disable the compressed decoder and does not allow the misaligned jump of pc
*/
parameter [0:0] COMPRESSED = 0,
/*
ALIGN = 1: Fetch the aligned instruction by making two bus transactions if the misaligned address
is given to the instruction bus.
*/
parameter [0:0] ALIGN = COMPRESSED,
/* Multiplication and Division Unit
This parameter enables the interface for connecting SERV and MDU
*/
parameter [0:0] MDU = 0,
/* Register signals before or after the decoder
0 : Register after the decoder. Faster but uses more resources
1 : (default) Register before the decoder. Slower but uses less resources
*/
parameter PRE_REGISTER = 1,
/* Amount of reset applied to design
"NONE" : No reset at all. Relies on a POR to set correct initialization
values and that core isn't reset during runtime
"MINI" : Standard setting. Resets the minimal amount of FFs needed to
restart execution from the instruction at RESET_PC
*/
parameter RESET_STRATEGY = "MINI",
parameter [0:0] DEBUG = 1'b0,
parameter WITH_CSR = 1,
parameter W = 1,
parameter RF_WIDTH = W * 2,
parameter RF_L2D = `CLOG2((32+(WITH_CSR*4))*32/RF_WIDTH))
(
input wire clk,
input wire i_rst,
input wire i_timer_irq,
`ifdef RISCV_FORMAL
output wire rvfi_valid,
output wire [63:0] rvfi_order,
output wire [31:0] rvfi_insn,
output wire rvfi_trap,
output wire rvfi_halt,
output wire rvfi_intr,
output wire [1:0] rvfi_mode,
output wire [1:0] rvfi_ixl,
output wire [4:0] rvfi_rs1_addr,
output wire [4:0] rvfi_rs2_addr,
output wire [31:0] rvfi_rs1_rdata,
output wire [31:0] rvfi_rs2_rdata,
output wire [4:0] rvfi_rd_addr,
output wire [31:0] rvfi_rd_wdata,
output wire [31:0] rvfi_pc_rdata,
output wire [31:0] rvfi_pc_wdata,
output wire [31:0] rvfi_mem_addr,
output wire [3:0] rvfi_mem_rmask,
output wire [3:0] rvfi_mem_wmask,
output wire [31:0] rvfi_mem_rdata,
output wire [31:0] rvfi_mem_wdata,
`endif
output wire [31:0] o_ibus_adr,
output wire o_ibus_cyc,
input wire [31:0] i_ibus_rdt,
input wire i_ibus_ack,
output wire [31:0] o_dbus_adr,
output wire [31:0] o_dbus_dat,
output wire [3:0] o_dbus_sel,
output wire o_dbus_we ,
output wire o_dbus_cyc,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack,
// Extension
output wire [31:0] o_ext_rs1,
output wire [31:0] o_ext_rs2,
output wire [ 2:0] o_ext_funct3,
input wire [31:0] i_ext_rd,
input wire i_ext_ready,
// MDU
output wire o_mdu_valid);
localparam CSR_REGS = WITH_CSR*4;
wire rf_wreq;
wire rf_rreq;
wire [4+WITH_CSR:0] wreg0;
wire [4+WITH_CSR:0] wreg1;
wire wen0;
wire wen1;
wire [W-1:0] wdata0;
wire [W-1:0] wdata1;
wire [4+WITH_CSR:0] rreg0;
wire [4+WITH_CSR:0] rreg1;
wire rf_ready;
wire [W-1:0] rdata0;
wire [W-1:0] rdata1;
wire [RF_L2D-1:0] waddr;
wire [RF_WIDTH-1:0] wdata;
wire wen;
wire [RF_L2D-1:0] raddr;
wire ren;
wire [RF_WIDTH-1:0] rdata;
serv_rf_ram_if
#(.width (RF_WIDTH),
.reset_strategy (RESET_STRATEGY),
.csr_regs (CSR_REGS),
.W(W))
rf_ram_if
(.i_clk (clk),
.i_rst (i_rst),
.i_wreq (rf_wreq),
.i_rreq (rf_rreq),
.o_ready (rf_ready),
.i_wreg0 (wreg0),
.i_wreg1 (wreg1),
.i_wen0 (wen0),
.i_wen1 (wen1),
.i_wdata0 (wdata0),
.i_wdata1 (wdata1),
.i_rreg0 (rreg0),
.i_rreg1 (rreg1),
.o_rdata0 (rdata0),
.o_rdata1 (rdata1),
.o_waddr (waddr),
.o_wdata (wdata),
.o_wen (wen),
.o_raddr (raddr),
.o_ren (ren),
.i_rdata (rdata));
serv_rf_ram
#(.width (RF_WIDTH),
.csr_regs (CSR_REGS))
rf_ram
(.i_clk (clk),
.i_waddr (waddr),
.i_wdata (wdata),
.i_wen (wen),
.i_raddr (raddr),
.i_ren (ren),
.o_rdata (rdata));
qerv_top
#(.RESET_PC (RESET_PC),
.PRE_REGISTER (PRE_REGISTER),
.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR),
.DEBUG (DEBUG),
.MDU(MDU),
.COMPRESSED(COMPRESSED),
.ALIGN(ALIGN),
.W(W))
cpu
(
.clk (clk),
.i_rst (i_rst),
.i_timer_irq (i_timer_irq),
`ifdef RISCV_FORMAL
.rvfi_valid (rvfi_valid ),
.rvfi_order (rvfi_order ),
.rvfi_insn (rvfi_insn ),
.rvfi_trap (rvfi_trap ),
.rvfi_halt (rvfi_halt ),
.rvfi_intr (rvfi_intr ),
.rvfi_mode (rvfi_mode ),
.rvfi_ixl (rvfi_ixl ),
.rvfi_rs1_addr (rvfi_rs1_addr ),
.rvfi_rs2_addr (rvfi_rs2_addr ),
.rvfi_rs1_rdata (rvfi_rs1_rdata),
.rvfi_rs2_rdata (rvfi_rs2_rdata),
.rvfi_rd_addr (rvfi_rd_addr ),
.rvfi_rd_wdata (rvfi_rd_wdata ),
.rvfi_pc_rdata (rvfi_pc_rdata ),
.rvfi_pc_wdata (rvfi_pc_wdata ),
.rvfi_mem_addr (rvfi_mem_addr ),
.rvfi_mem_rmask (rvfi_mem_rmask),
.rvfi_mem_wmask (rvfi_mem_wmask),
.rvfi_mem_rdata (rvfi_mem_rdata),
.rvfi_mem_wdata (rvfi_mem_wdata),
`endif
.o_rf_rreq (rf_rreq),
.o_rf_wreq (rf_wreq),
.i_rf_ready (rf_ready),
.o_wreg0 (wreg0),
.o_wreg1 (wreg1),
.o_wen0 (wen0),
.o_wen1 (wen1),
.o_wdata0 (wdata0),
.o_wdata1 (wdata1),
.o_rreg0 (rreg0),
.o_rreg1 (rreg1),
.i_rdata0 (rdata0),
.i_rdata1 (rdata1),
.o_ibus_adr (o_ibus_adr),
.o_ibus_cyc (o_ibus_cyc),
.i_ibus_rdt (i_ibus_rdt),
.i_ibus_ack (i_ibus_ack),
.o_dbus_adr (o_dbus_adr),
.o_dbus_dat (o_dbus_dat),
.o_dbus_sel (o_dbus_sel),
.o_dbus_we (o_dbus_we),
.o_dbus_cyc (o_dbus_cyc),
.i_dbus_rdt (i_dbus_rdt),
.i_dbus_ack (i_dbus_ack),
//Extension
.o_ext_funct3 (o_ext_funct3),
.i_ext_ready (i_ext_ready),
.i_ext_rd (i_ext_rd),
.o_ext_rs1 (o_ext_rs1),
.o_ext_rs2 (o_ext_rs2),
//MDU
.o_mdu_valid (o_mdu_valid));
endmodule
`default_nettype wire

View File

@@ -0,0 +1,136 @@
`default_nettype none
`include "../util/clog2.vh"
module serv_synth_wrapper
#(
/* Register signals before or after the decoder
0 : Register after the decoder. Faster but uses more resources
1 : (default) Register before the decoder. Slower but uses less resources
*/
parameter PRE_REGISTER = 1,
/* Amount of reset applied to design
"NONE" : No reset at all. Relies on a POR to set correct initialization
values and that core isn't reset during runtime
"MINI" : Standard setting. Resets the minimal amount of FFs needed to
restart execution from the instruction at RESET_PC
*/
parameter RESET_STRATEGY = "MINI",
parameter WITH_CSR = 1,
parameter RF_WIDTH = 8,
parameter RF_L2D = `CLOG2((32+(WITH_CSR*4))*32/RF_WIDTH))
(
input wire clk,
input wire i_rst,
input wire i_timer_irq,
output wire [31:0] o_ibus_adr,
output wire o_ibus_cyc,
input wire [31:0] i_ibus_rdt,
input wire i_ibus_ack,
output wire [31:0] o_dbus_adr,
output wire [31:0] o_dbus_dat,
output wire [3:0] o_dbus_sel,
output wire o_dbus_we ,
output wire o_dbus_cyc,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack,
output wire [RF_L2D-1:0] o_waddr,
output wire [RF_WIDTH-1:0] o_wdata,
output wire o_wen,
output wire [RF_L2D-1:0] o_raddr,
input wire [RF_WIDTH-1:0] i_rdata);
localparam CSR_REGS = WITH_CSR*4;
localparam W = 4;
wire rf_wreq;
wire rf_rreq;
wire [4+WITH_CSR:0] wreg0;
wire [4+WITH_CSR:0] wreg1;
wire wen0;
wire wen1;
wire [W-1:0] wdata0;
wire [W-1:0] wdata1;
wire [4+WITH_CSR:0] rreg0;
wire [4+WITH_CSR:0] rreg1;
wire rf_ready;
wire [W-1:0] rdata0;
wire [W-1:0] rdata1;
serv_rf_ram_if
#(.width (RF_WIDTH),
.reset_strategy (RESET_STRATEGY),
.W (W),
.csr_regs (CSR_REGS))
rf_ram_if
(.i_clk (clk),
.i_rst (i_rst),
.i_wreq (rf_wreq),
.i_rreq (rf_rreq),
.o_ready (rf_ready),
.i_wreg0 (wreg0),
.i_wreg1 (wreg1),
.i_wen0 (wen0),
.i_wen1 (wen1),
.i_wdata0 (wdata0),
.i_wdata1 (wdata1),
.i_rreg0 (rreg0),
.i_rreg1 (rreg1),
.o_rdata0 (rdata0),
.o_rdata1 (rdata1),
.o_waddr (o_waddr),
.o_wdata (o_wdata),
.o_wen (o_wen),
.o_raddr (o_raddr),
.i_rdata (i_rdata));
qerv_top
#(.RESET_PC (32'd0),
.PRE_REGISTER (PRE_REGISTER),
.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR),
.W (W),
.MDU(1'b0))
cpu
(
.clk (clk),
.i_rst (i_rst),
.i_timer_irq (i_timer_irq),
.o_rf_rreq (rf_rreq),
.o_rf_wreq (rf_wreq),
.i_rf_ready (rf_ready),
.o_wreg0 (wreg0),
.o_wreg1 (wreg1),
.o_wen0 (wen0),
.o_wen1 (wen1),
.o_wdata0 (wdata0),
.o_wdata1 (wdata1),
.o_rreg0 (rreg0),
.o_rreg1 (rreg1),
.i_rdata0 (rdata0),
.i_rdata1 (rdata1),
.o_ibus_adr (o_ibus_adr),
.o_ibus_cyc (o_ibus_cyc),
.i_ibus_rdt (i_ibus_rdt),
.i_ibus_ack (i_ibus_ack),
.o_dbus_adr (o_dbus_adr),
.o_dbus_dat (o_dbus_dat),
.o_dbus_sel (o_dbus_sel),
.o_dbus_we (o_dbus_we),
.o_dbus_cyc (o_dbus_cyc),
.i_dbus_rdt (i_dbus_rdt),
.i_dbus_ack (i_dbus_ack),
//Extension
.o_ext_funct3 (),
.i_ext_ready (1'b0),
.i_ext_rd (32'd0),
.o_ext_rs1 (),
.o_ext_rs2 (),
//MDU
.o_mdu_valid ());
endmodule
`default_nettype wire

694
rtl/qerv/serv_top.v Normal file
View File

@@ -0,0 +1,694 @@
`default_nettype none
module qerv_top
#(parameter WITH_CSR = 1,
parameter W = 1,
parameter B = W-1,
parameter PRE_REGISTER = 1,
parameter RESET_STRATEGY = "MINI",
parameter RESET_PC = 32'd0,
parameter [0:0] DEBUG = 1'b0,
parameter [0:0] MDU = 1'b0,
parameter [0:0] COMPRESSED=0,
parameter [0:0] ALIGN = COMPRESSED)
(
input wire clk,
input wire i_rst,
input wire i_timer_irq,
`ifdef RISCV_FORMAL
output wire rvfi_valid,
output wire [63:0] rvfi_order,
output wire [31:0] rvfi_insn,
output wire rvfi_trap,
output wire rvfi_halt,
output wire rvfi_intr,
output wire [1:0] rvfi_mode,
output wire [1:0] rvfi_ixl,
output wire [4:0] rvfi_rs1_addr,
output wire [4:0] rvfi_rs2_addr,
output wire [31:0] rvfi_rs1_rdata,
output wire [31:0] rvfi_rs2_rdata,
output wire [4:0] rvfi_rd_addr,
output wire [31:0] rvfi_rd_wdata,
output wire [31:0] rvfi_pc_rdata,
output wire [31:0] rvfi_pc_wdata,
output wire [31:0] rvfi_mem_addr,
output wire [3:0] rvfi_mem_rmask,
output wire [3:0] rvfi_mem_wmask,
output wire [31:0] rvfi_mem_rdata,
output wire [31:0] rvfi_mem_wdata,
`endif
//RF Interface
output wire o_rf_rreq,
output wire o_rf_wreq,
input wire i_rf_ready,
output wire [4+WITH_CSR:0] o_wreg0,
output wire [4+WITH_CSR:0] o_wreg1,
output wire o_wen0,
output wire o_wen1,
output wire [B:0] o_wdata0,
output wire [B:0] o_wdata1,
output wire [4+WITH_CSR:0] o_rreg0,
output wire [4+WITH_CSR:0] o_rreg1,
input wire [B:0] i_rdata0,
input wire [B:0] i_rdata1,
output wire [31:0] o_ibus_adr,
output wire o_ibus_cyc,
input wire [31:0] i_ibus_rdt,
input wire i_ibus_ack,
output wire [31:0] o_dbus_adr,
output wire [31:0] o_dbus_dat,
output wire [3:0] o_dbus_sel,
output wire o_dbus_we ,
output wire o_dbus_cyc,
input wire [31:0] i_dbus_rdt,
input wire i_dbus_ack,
//Extension
output wire [ 2:0] o_ext_funct3,
input wire i_ext_ready,
input wire [31:0] i_ext_rd,
output wire [31:0] o_ext_rs1,
output wire [31:0] o_ext_rs2,
//MDU
output wire o_mdu_valid);
wire [4:0] rd_addr;
wire [4:0] rs1_addr;
wire [4:0] rs2_addr;
wire [3:0] immdec_ctrl;
wire [3:0] immdec_en;
wire sh_right;
wire bne_or_bge;
wire cond_branch;
wire two_stage_op;
wire e_op;
wire ebreak;
wire branch_op;
wire shift_op;
wire rd_op;
wire mdu_op;
wire rd_alu_en;
wire rd_csr_en;
wire rd_mem_en;
wire [B:0] ctrl_rd;
wire [B:0] alu_rd;
wire [B:0] mem_rd;
wire [B:0] csr_rd;
wire mtval_pc;
wire ctrl_pc_en;
wire jump;
wire jal_or_jalr;
wire utype;
wire mret;
wire [B:0] imm;
wire trap;
wire pc_rel;
wire iscomp;
wire init;
wire cnt_en;
wire cnt0to3;
wire cnt12to31;
wire cnt0;
wire cnt1;
wire cnt2;
wire cnt3;
wire cnt7;
wire cnt11;
wire cnt12;
wire cnt_done;
wire bufreg_en;
wire bufreg_sh_signed;
wire bufreg_rs1_en;
wire bufreg_imm_en;
wire bufreg_clr_lsb;
wire [B:0] bufreg_q;
wire [B:0] bufreg2_q;
wire [31:0] dbus_rdt;
wire dbus_ack;
wire alu_sub;
wire [1:0] alu_bool_op;
wire alu_cmp_eq;
wire alu_cmp_sig;
wire alu_cmp;
wire [2:0] alu_rd_sel;
wire [B:0] rs1;
wire [B:0] rs2;
wire rd_en;
wire [B:0] op_b;
wire op_b_sel;
wire mem_signed;
wire mem_word;
wire mem_half;
wire [1:0] mem_bytecnt;
wire sh_done;
wire mem_misalign;
wire [B:0] bad_pc;
wire csr_mstatus_en;
wire csr_mie_en;
wire csr_mcause_en;
wire [1:0] csr_source;
wire [B:0] csr_imm;
wire csr_d_sel;
wire csr_en;
wire [1:0] csr_addr;
wire [B:0] csr_pc;
wire csr_imm_en;
wire [B:0] csr_in;
wire [B:0] rf_csr_out;
wire dbus_en;
wire new_irq;
wire [1:0] lsb;
//verilator lint_off UNUSED
wire [31:0] i_wb_rdt;
//verilator lint_on UNUSED
wire [31:0] wb_ibus_adr;
wire wb_ibus_cyc;
wire [31:0] wb_ibus_rdt;
wire wb_ibus_ack;
generate
if (ALIGN) begin : gen_align
serv_aligner align
(
.clk(clk),
.rst(i_rst),
// serv_rf_top
.i_ibus_adr(wb_ibus_adr),
.i_ibus_cyc(wb_ibus_cyc),
.o_ibus_rdt(wb_ibus_rdt),
.o_ibus_ack(wb_ibus_ack),
// servant_arbiter
.o_wb_ibus_adr(o_ibus_adr),
.o_wb_ibus_cyc(o_ibus_cyc),
.i_wb_ibus_rdt(i_ibus_rdt),
.i_wb_ibus_ack(i_ibus_ack));
end else begin : gen_no_align
assign o_ibus_adr = wb_ibus_adr;
assign o_ibus_cyc = wb_ibus_cyc;
assign wb_ibus_rdt = i_ibus_rdt;
assign wb_ibus_ack = i_ibus_ack;
end
endgenerate
generate
if (COMPRESSED) begin : gen_compressed
serv_compdec compdec
(
.i_clk(clk),
.i_instr(wb_ibus_rdt),
.i_ack(wb_ibus_ack),
.o_instr(i_wb_rdt),
.o_iscomp(iscomp));
end else begin : gen_no_compressed
assign i_wb_rdt = wb_ibus_rdt;
assign iscomp = 1'b0;
end
endgenerate
serv_state
#(.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR[0:0]),
.MDU(MDU),
.ALIGN(ALIGN),
.W(W))
state
(
.i_clk (clk),
.i_rst (i_rst),
//State
.i_new_irq (new_irq),
.i_alu_cmp (alu_cmp),
.o_init (init),
.o_cnt_en (cnt_en),
.o_cnt0to3 (cnt0to3),
.o_cnt12to31 (cnt12to31),
.o_cnt0 (cnt0),
.o_cnt1 (cnt1),
.o_cnt2 (cnt2),
.o_cnt3 (cnt3),
.o_cnt7 (cnt7),
.o_cnt11 (cnt11),
.o_cnt12 (cnt12),
.o_cnt_done (cnt_done),
.o_bufreg_en (bufreg_en),
.o_ctrl_pc_en (ctrl_pc_en),
.o_ctrl_jump (jump),
.o_ctrl_trap (trap),
.i_ctrl_misalign(lsb[1]),
.i_sh_done (sh_done),
.o_mem_bytecnt (mem_bytecnt),
.i_mem_misalign (mem_misalign),
//Control
.i_bne_or_bge (bne_or_bge),
.i_cond_branch (cond_branch),
.i_dbus_en (dbus_en),
.i_two_stage_op (two_stage_op),
.i_branch_op (branch_op),
.i_shift_op (shift_op),
.i_sh_right (sh_right),
.i_alu_rd_sel1 (alu_rd_sel[1]),
.i_rd_alu_en (rd_alu_en),
.i_e_op (e_op),
.i_rd_op (rd_op),
//MDU
.i_mdu_op (mdu_op),
.o_mdu_valid (o_mdu_valid),
//Extension
.i_mdu_ready (i_ext_ready),
//External
.o_dbus_cyc (o_dbus_cyc),
.i_dbus_ack (i_dbus_ack),
.o_ibus_cyc (wb_ibus_cyc),
.i_ibus_ack (wb_ibus_ack),
//RF Interface
.o_rf_rreq (o_rf_rreq),
.o_rf_wreq (o_rf_wreq),
.i_rf_ready (i_rf_ready),
.o_rf_rd_en (rd_en));
serv_decode
#(.PRE_REGISTER (PRE_REGISTER),
.MDU(MDU))
decode
(
.clk (clk),
//Input
.i_wb_rdt (i_wb_rdt[31:2]),
.i_wb_en (wb_ibus_ack),
//To state
.o_bne_or_bge (bne_or_bge),
.o_cond_branch (cond_branch),
.o_dbus_en (dbus_en),
.o_e_op (e_op),
.o_ebreak (ebreak),
.o_branch_op (branch_op),
.o_shift_op (shift_op),
.o_rd_op (rd_op),
.o_sh_right (sh_right),
.o_mdu_op (mdu_op),
.o_two_stage_op (two_stage_op),
//Extension
.o_ext_funct3 (o_ext_funct3),
//To bufreg
.o_bufreg_rs1_en (bufreg_rs1_en),
.o_bufreg_imm_en (bufreg_imm_en),
.o_bufreg_clr_lsb (bufreg_clr_lsb),
.o_bufreg_sh_signed (bufreg_sh_signed),
//To bufreg2
.o_op_b_source (op_b_sel),
//To ctrl
.o_ctrl_jal_or_jalr (jal_or_jalr),
.o_ctrl_utype (utype),
.o_ctrl_pc_rel (pc_rel),
.o_ctrl_mret (mret),
//To alu
.o_alu_sub (alu_sub),
.o_alu_bool_op (alu_bool_op),
.o_alu_cmp_eq (alu_cmp_eq),
.o_alu_cmp_sig (alu_cmp_sig),
.o_alu_rd_sel (alu_rd_sel),
//To mem IF
.o_mem_cmd (o_dbus_we),
.o_mem_signed (mem_signed),
.o_mem_word (mem_word),
.o_mem_half (mem_half),
//To CSR
.o_csr_en (csr_en),
.o_csr_addr (csr_addr),
.o_csr_mstatus_en (csr_mstatus_en),
.o_csr_mie_en (csr_mie_en),
.o_csr_mcause_en (csr_mcause_en),
.o_csr_source (csr_source),
.o_csr_d_sel (csr_d_sel),
.o_csr_imm_en (csr_imm_en),
.o_mtval_pc (mtval_pc ),
//To top
.o_immdec_ctrl (immdec_ctrl),
.o_immdec_en (immdec_en),
//To RF IF
.o_rd_mem_en (rd_mem_en),
.o_rd_csr_en (rd_csr_en),
.o_rd_alu_en (rd_alu_en));
generate
if (W == 1) begin : gen_serv_immdec
serv_immdec immdec
(
.i_clk (clk),
//State
.i_cnt_en (cnt_en),
.i_cnt_done (cnt_done),
//Control
.i_immdec_en (immdec_en),
.i_csr_imm_en (csr_imm_en),
.i_ctrl (immdec_ctrl),
.o_rd_addr (rd_addr),
.o_rs1_addr (rs1_addr),
.o_rs2_addr (rs2_addr),
//Data
.o_csr_imm (csr_imm),
.o_imm (imm),
//External
.i_wb_en (wb_ibus_ack),
.i_wb_rdt (i_wb_rdt[31:7]));
end else if (W == 4) begin : gen_qerv_immdec
qerv_immdec immdec
(
.i_clk (clk),
//State
.i_cnt_en (cnt_en),
.i_cnt_done (cnt_done),
//Control
.i_immdec_en (immdec_en),
.i_csr_imm_en (csr_imm_en),
.i_ctrl (immdec_ctrl),
.o_rd_addr (rd_addr),
.o_rs1_addr (rs1_addr),
.o_rs2_addr (rs2_addr),
//Data
.o_csr_imm (csr_imm),
.o_imm (imm),
//External
.i_wb_en (wb_ibus_ack),
.i_wb_rdt (i_wb_rdt[31:7]));
end
endgenerate
serv_bufreg
#(.MDU(MDU),
.W(W))
bufreg
(
.i_clk (clk),
//State
.i_cnt0 (cnt0),
.i_cnt1 (cnt1),
.i_cnt_done (cnt_done),
.i_en (bufreg_en),
.i_init (init),
.i_mdu_op (mdu_op),
.o_lsb (lsb),
//Control
.i_sh_signed (bufreg_sh_signed),
.i_rs1_en (bufreg_rs1_en),
.i_imm_en (bufreg_imm_en),
.i_clr_lsb (bufreg_clr_lsb),
.i_shift_op (shift_op),
.i_right_shift_op (sh_right),
.i_shamt (o_dbus_dat[26:24]),
//Data
.i_rs1 (rs1),
.i_imm (imm),
.o_q (bufreg_q),
//External
.o_dbus_adr (o_dbus_adr),
.o_ext_rs1 (o_ext_rs1));
serv_bufreg2 #(.W(W)) bufreg2
(
.i_clk (clk),
//State
.i_en (cnt_en),
.i_init (init),
.i_cnt7 (cnt7),
.i_cnt_done (cnt_done),
.i_sh_right (sh_right),
.i_lsb (lsb),
.i_bytecnt (mem_bytecnt),
.o_sh_done (sh_done),
//Control
.i_op_b_sel (op_b_sel),
.i_shift_op (shift_op),
//Data
.i_rs2 (rs2),
.i_imm (imm),
.o_op_b (op_b),
.o_q (bufreg2_q),
//External
.o_dat (o_dbus_dat),
.i_load (dbus_ack),
.i_dat (dbus_rdt));
serv_ctrl
#(.RESET_PC (RESET_PC),
.RESET_STRATEGY (RESET_STRATEGY),
.WITH_CSR (WITH_CSR),
.W (W))
ctrl
(
.clk (clk),
.i_rst (i_rst),
//State
.i_pc_en (ctrl_pc_en),
.i_cnt12to31 (cnt12to31),
.i_cnt0 (cnt0),
.i_cnt1 (cnt1),
.i_cnt2 (cnt2),
//Control
.i_jump (jump),
.i_jal_or_jalr (jal_or_jalr),
.i_utype (utype),
.i_pc_rel (pc_rel),
.i_trap (trap | mret),
.i_iscomp (iscomp),
//Data
.i_imm (imm),
.i_buf (bufreg_q),
.i_csr_pc (csr_pc),
.o_rd (ctrl_rd),
.o_bad_pc (bad_pc),
//External
.o_ibus_adr (wb_ibus_adr));
serv_alu #(.W (W)) alu
(
.clk (clk),
//State
.i_en (cnt_en),
.i_cnt0 (cnt0),
.o_cmp (alu_cmp),
//Control
.i_sub (alu_sub),
.i_bool_op (alu_bool_op),
.i_cmp_eq (alu_cmp_eq),
.i_cmp_sig (alu_cmp_sig),
.i_rd_sel (alu_rd_sel),
//Data
.i_rs1 (rs1),
.i_op_b (op_b),
.i_buf (bufreg_q),
.o_rd (alu_rd));
serv_rf_if
#(.WITH_CSR (WITH_CSR), .W(W))
rf_if
(//RF interface
.i_cnt_en (cnt_en),
.o_wreg0 (o_wreg0),
.o_wreg1 (o_wreg1),
.o_wen0 (o_wen0),
.o_wen1 (o_wen1),
.o_wdata0 (o_wdata0),
.o_wdata1 (o_wdata1),
.o_rreg0 (o_rreg0),
.o_rreg1 (o_rreg1),
.i_rdata0 (i_rdata0),
.i_rdata1 (i_rdata1),
//Trap interface
.i_trap (trap),
.i_mret (mret),
.i_mepc (wb_ibus_adr[B:0]),
.i_mtval_pc (mtval_pc),
.i_bufreg_q (bufreg_q),
.i_bad_pc (bad_pc),
.o_csr_pc (csr_pc),
//CSR write port
.i_csr_en (csr_en),
.i_csr_addr (csr_addr),
.i_csr (csr_in),
//RD write port
.i_rd_wen (rd_en),
.i_rd_waddr (rd_addr),
.i_ctrl_rd (ctrl_rd),
.i_alu_rd (alu_rd),
.i_rd_alu_en (rd_alu_en),
.i_csr_rd (csr_rd),
.i_rd_csr_en (rd_csr_en),
.i_mem_rd (mem_rd),
.i_rd_mem_en (rd_mem_en),
//RS1 read port
.i_rs1_raddr (rs1_addr),
.o_rs1 (rs1),
//RS2 read port
.i_rs2_raddr (rs2_addr),
.o_rs2 (rs2),
//CSR read port
.o_csr (rf_csr_out));
serv_mem_if
#(.WITH_CSR (WITH_CSR[0:0]),
.W (W))
mem_if
(
.i_clk (clk),
//State
.i_bytecnt (mem_bytecnt),
.i_lsb (lsb),
.o_misalign (mem_misalign),
//Control
.i_mdu_op (mdu_op),
.i_signed (mem_signed),
.i_word (mem_word),
.i_half (mem_half),
//Data
.i_bufreg2_q (bufreg2_q),
.o_rd (mem_rd),
//External interface
.o_wb_sel (o_dbus_sel));
generate
if (|WITH_CSR) begin : gen_csr
serv_csr
#(.RESET_STRATEGY (RESET_STRATEGY),
.W(W))
csr
(
.i_clk (clk),
.i_rst (i_rst),
//State
.i_trig_irq (wb_ibus_ack),
.i_en (cnt_en),
.i_cnt0to3 (cnt0to3),
.i_cnt3 (cnt3),
.i_cnt7 (cnt7),
.i_cnt11 (cnt11),
.i_cnt12 (cnt12),
.i_cnt_done (cnt_done),
.i_mem_op (!mtval_pc),
.i_mtip (i_timer_irq),
.i_trap (trap),
.o_new_irq (new_irq),
//Control
.i_e_op (e_op),
.i_ebreak (ebreak),
.i_mem_cmd (o_dbus_we),
.i_mstatus_en (csr_mstatus_en),
.i_mie_en (csr_mie_en ),
.i_mcause_en (csr_mcause_en ),
.i_csr_source (csr_source),
.i_mret (mret),
.i_csr_d_sel (csr_d_sel),
//Data
.i_rf_csr_out (rf_csr_out),
.o_csr_in (csr_in),
.i_csr_imm (csr_imm),
.i_rs1 (rs1),
.o_q (csr_rd));
end else begin : gen_no_csr
assign csr_in = {W{1'b0}};
assign csr_rd = {W{1'b0}};
assign new_irq = 1'b0;
end
endgenerate
generate
if (DEBUG) begin : gen_debug
serv_debug #(.W (W), .RESET_PC (RESET_PC)) debug
(
`ifdef RISCV_FORMAL
.rvfi_valid (rvfi_valid ),
.rvfi_order (rvfi_order ),
.rvfi_insn (rvfi_insn ),
.rvfi_trap (rvfi_trap ),
.rvfi_halt (rvfi_halt ),
.rvfi_intr (rvfi_intr ),
.rvfi_mode (rvfi_mode ),
.rvfi_ixl (rvfi_ixl ),
.rvfi_rs1_addr (rvfi_rs1_addr ),
.rvfi_rs2_addr (rvfi_rs2_addr ),
.rvfi_rs1_rdata (rvfi_rs1_rdata),
.rvfi_rs2_rdata (rvfi_rs2_rdata),
.rvfi_rd_addr (rvfi_rd_addr ),
.rvfi_rd_wdata (rvfi_rd_wdata ),
.rvfi_pc_rdata (rvfi_pc_rdata ),
.rvfi_pc_wdata (rvfi_pc_wdata ),
.rvfi_mem_addr (rvfi_mem_addr ),
.rvfi_mem_rmask (rvfi_mem_rmask),
.rvfi_mem_wmask (rvfi_mem_wmask),
.rvfi_mem_rdata (rvfi_mem_rdata),
.rvfi_mem_wdata (rvfi_mem_wdata),
.i_dbus_adr (o_dbus_adr),
.i_dbus_dat (o_dbus_dat),
.i_dbus_sel (o_dbus_sel),
.i_dbus_we (o_dbus_we ),
.i_dbus_rdt (i_dbus_rdt),
.i_dbus_ack (i_dbus_ack),
.i_ctrl_pc_en (ctrl_pc_en),
.rs1 (rs1),
.rs2 (rs2),
.rs1_addr (rs1_addr),
.rs2_addr (rs2_addr),
.immdec_en (immdec_en),
.rd_en (rd_en),
.trap (trap),
.i_rf_ready (i_rf_ready),
.i_ibus_cyc (o_ibus_cyc),
.two_stage_op (two_stage_op),
.init (init),
.i_ibus_adr (o_ibus_adr),
`endif
.i_clk (clk),
.i_rst (i_rst),
.i_ibus_rdt (i_ibus_rdt),
.i_ibus_ack (i_ibus_ack),
.i_rd_addr (rd_addr ),
.i_cnt_en (cnt_en ),
.i_csr_in (csr_in ),
.i_csr_mstatus_en (csr_mstatus_en),
.i_csr_mie_en (csr_mie_en ),
.i_csr_mcause_en (csr_mcause_en ),
.i_csr_en (csr_en ),
.i_csr_addr (csr_addr),
.i_wen0 (o_wen0),
.i_wdata0 (o_wdata0),
.i_cnt_done (cnt_done));
end
endgenerate
generate
if (MDU) begin: gen_mdu
assign dbus_rdt = i_ext_ready ? i_ext_rd:i_dbus_rdt;
assign dbus_ack = i_dbus_ack | i_ext_ready;
end else begin : gen_no_mdu
assign dbus_rdt = i_dbus_rdt;
assign dbus_ack = i_dbus_ack;
end
assign o_ext_rs2 = o_dbus_dat;
endgenerate
endmodule
`default_nettype wire

279
rtl/qerv/servile.v Normal file
View File

@@ -0,0 +1,279 @@
/*
* servile.v : Top-level for Servile, the SERV convenience wrapper
*
* SPDX-FileCopyrightText: 2024 Olof Kindgren <olof.kindgren@gmail.com>
* SPDX-License-Identifier: Apache-2.0
*/
`default_nettype none
module servile
#(
parameter width = 1,
parameter reset_pc = 32'h00000000,
parameter reset_strategy = "MINI",
parameter rf_width = 2*width,
parameter [0:0] sim = 1'b0,
parameter [0:0] debug = 1'b0,
parameter [0:0] with_c = 1'b0,
parameter [0:0] with_csr = 1'b0,
parameter [0:0] with_mdu = 1'b0,
//Internally calculated. Do not touch
parameter B = width-1,
parameter regs = 32+with_csr*4,
parameter rf_l2d = $clog2(regs*32/rf_width))
(
input wire i_clk,
input wire i_rst,
input wire i_timer_irq,
//Memory (WB) interface
output wire [31:0] o_wb_mem_adr,
output wire [31:0] o_wb_mem_dat,
output wire [3:0] o_wb_mem_sel,
output wire o_wb_mem_we ,
output wire o_wb_mem_stb,
input wire [31:0] i_wb_mem_rdt,
input wire i_wb_mem_ack,
//Extension (WB) interface
output wire [31:0] o_wb_ext_adr,
output wire [31:0] o_wb_ext_dat,
output wire [3:0] o_wb_ext_sel,
output wire o_wb_ext_we ,
output wire o_wb_ext_stb,
input wire [31:0] i_wb_ext_rdt,
input wire i_wb_ext_ack,
//RF (SRAM) interface
output wire [rf_l2d-1:0] o_rf_waddr,
output wire [rf_width-1:0] o_rf_wdata,
output wire o_rf_wen,
output wire [rf_l2d-1:0] o_rf_raddr,
input wire [rf_width-1:0] i_rf_rdata,
output wire o_rf_ren);
wire [31:0] wb_ibus_adr;
wire wb_ibus_stb;
wire [31:0] wb_ibus_rdt;
wire wb_ibus_ack;
wire [31:0] wb_dbus_adr;
wire [31:0] wb_dbus_dat;
wire [3:0] wb_dbus_sel;
wire wb_dbus_we;
wire wb_dbus_stb;
wire [31:0] wb_dbus_rdt;
wire wb_dbus_ack;
wire [31:0] wb_dmem_adr;
wire [31:0] wb_dmem_dat;
wire [3:0] wb_dmem_sel;
wire wb_dmem_we;
wire wb_dmem_stb;
wire [31:0] wb_dmem_rdt;
wire wb_dmem_ack;
wire rf_wreq;
wire rf_rreq;
wire [$clog2(regs)-1:0] wreg0;
wire [$clog2(regs)-1:0] wreg1;
wire wen0;
wire wen1;
wire [B:0] wdata0;
wire [B:0] wdata1;
wire [$clog2(regs)-1:0] rreg0;
wire [$clog2(regs)-1:0] rreg1;
wire rf_ready;
wire [B:0] rdata0;
wire [B:0] rdata1;
wire [31:0] mdu_rs1;
wire [31:0] mdu_rs2;
wire [ 2:0] mdu_op;
wire mdu_valid;
wire [31:0] mdu_rd;
wire mdu_ready;
servile_mux
#(.sim (sim))
mux
(.i_clk (i_clk),
.i_rst (i_rst & (reset_strategy != "NONE")),
.i_wb_cpu_adr (wb_dbus_adr),
.i_wb_cpu_dat (wb_dbus_dat),
.i_wb_cpu_sel (wb_dbus_sel),
.i_wb_cpu_we (wb_dbus_we),
.i_wb_cpu_stb (wb_dbus_stb),
.o_wb_cpu_rdt (wb_dbus_rdt),
.o_wb_cpu_ack (wb_dbus_ack),
.o_wb_mem_adr (wb_dmem_adr),
.o_wb_mem_dat (wb_dmem_dat),
.o_wb_mem_sel (wb_dmem_sel),
.o_wb_mem_we (wb_dmem_we),
.o_wb_mem_stb (wb_dmem_stb),
.i_wb_mem_rdt (wb_dmem_rdt),
.i_wb_mem_ack (wb_dmem_ack),
.o_wb_ext_adr (o_wb_ext_adr),
.o_wb_ext_dat (o_wb_ext_dat),
.o_wb_ext_sel (o_wb_ext_sel),
.o_wb_ext_we (o_wb_ext_we),
.o_wb_ext_stb (o_wb_ext_stb),
.i_wb_ext_rdt (i_wb_ext_rdt),
.i_wb_ext_ack (i_wb_ext_ack));
servile_arbiter arbiter
(.i_wb_cpu_dbus_adr (wb_dmem_adr),
.i_wb_cpu_dbus_dat (wb_dmem_dat),
.i_wb_cpu_dbus_sel (wb_dmem_sel),
.i_wb_cpu_dbus_we (wb_dmem_we ),
.i_wb_cpu_dbus_stb (wb_dmem_stb),
.o_wb_cpu_dbus_rdt (wb_dmem_rdt),
.o_wb_cpu_dbus_ack (wb_dmem_ack),
.i_wb_cpu_ibus_adr (wb_ibus_adr),
.i_wb_cpu_ibus_stb (wb_ibus_stb),
.o_wb_cpu_ibus_rdt (wb_ibus_rdt),
.o_wb_cpu_ibus_ack (wb_ibus_ack),
.o_wb_mem_adr (o_wb_mem_adr),
.o_wb_mem_dat (o_wb_mem_dat),
.o_wb_mem_sel (o_wb_mem_sel),
.o_wb_mem_we (o_wb_mem_we ),
.o_wb_mem_stb (o_wb_mem_stb),
.i_wb_mem_rdt (i_wb_mem_rdt),
.i_wb_mem_ack (i_wb_mem_ack));
serv_rf_ram_if
#(.width (rf_width),
.W (width),
.reset_strategy (reset_strategy),
.csr_regs (with_csr*4))
rf_ram_if
(.i_clk (i_clk),
.i_rst (i_rst),
//RF IF
.i_wreq (rf_wreq),
.i_rreq (rf_rreq),
.o_ready (rf_ready),
.i_wreg0 (wreg0),
.i_wreg1 (wreg1),
.i_wen0 (wen0),
.i_wen1 (wen1),
.i_wdata0 (wdata0),
.i_wdata1 (wdata1),
.i_rreg0 (rreg0),
.i_rreg1 (rreg1),
.o_rdata0 (rdata0),
.o_rdata1 (rdata1),
//SRAM IF
.o_waddr (o_rf_waddr),
.o_wdata (o_rf_wdata),
.o_wen (o_rf_wen),
.o_raddr (o_rf_raddr),
.o_ren (o_rf_ren),
.i_rdata (i_rf_rdata));
generate
if (with_mdu) begin : gen_mdu
mdu_top mdu_serv
(.i_clk (i_clk),
.i_rst (i_rst),
.i_mdu_rs1 (mdu_rs1),
.i_mdu_rs2 (mdu_rs2),
.i_mdu_op (mdu_op),
.i_mdu_valid (mdu_valid),
.o_mdu_ready (mdu_ready),
.o_mdu_rd (mdu_rd));
end else begin
assign mdu_ready = 1'b0;
assign mdu_rd = 32'd0;
end
endgenerate
qerv_top
#(
.WITH_CSR (with_csr?1:0),
.W (width),
.PRE_REGISTER (1'b1),
.RESET_STRATEGY (reset_strategy),
.RESET_PC (reset_pc),
.DEBUG (debug),
.MDU (with_mdu),
.COMPRESSED (with_c))
cpu
(
.clk (i_clk),
.i_rst (i_rst),
.i_timer_irq (i_timer_irq),
`ifdef RISCV_FORMAL
.rvfi_valid (),
.rvfi_order (),
.rvfi_insn (),
.rvfi_trap (),
.rvfi_halt (),
.rvfi_intr (),
.rvfi_mode (),
.rvfi_ixl (),
.rvfi_rs1_addr (),
.rvfi_rs2_addr (),
.rvfi_rs1_rdata (),
.rvfi_rs2_rdata (),
.rvfi_rd_addr (),
.rvfi_rd_wdata (),
.rvfi_pc_rdata (),
.rvfi_pc_wdata (),
.rvfi_mem_addr (),
.rvfi_mem_rmask (),
.rvfi_mem_wmask (),
.rvfi_mem_rdata (),
.rvfi_mem_wdata (),
`endif
//RF IF
.o_rf_rreq (rf_rreq),
.o_rf_wreq (rf_wreq),
.i_rf_ready (rf_ready),
.o_wreg0 (wreg0),
.o_wreg1 (wreg1),
.o_wen0 (wen0),
.o_wen1 (wen1),
.o_wdata0 (wdata0),
.o_wdata1 (wdata1),
.o_rreg0 (rreg0),
.o_rreg1 (rreg1),
.i_rdata0 (rdata0),
.i_rdata1 (rdata1),
//Instruction bus
.o_ibus_adr (wb_ibus_adr),
.o_ibus_cyc (wb_ibus_stb),
.i_ibus_rdt (wb_ibus_rdt),
.i_ibus_ack (wb_ibus_ack),
//Data bus
.o_dbus_adr (wb_dbus_adr),
.o_dbus_dat (wb_dbus_dat),
.o_dbus_sel (wb_dbus_sel),
.o_dbus_we (wb_dbus_we),
.o_dbus_cyc (wb_dbus_stb),
.i_dbus_rdt (wb_dbus_rdt),
.i_dbus_ack (wb_dbus_ack),
//Extension IF
.o_ext_rs1 (mdu_rs1),
.o_ext_rs2 (mdu_rs2),
.o_ext_funct3 (mdu_op),
.i_ext_rd (mdu_rd),
.i_ext_ready (mdu_ready),
//MDU
.o_mdu_valid (mdu_valid));
endmodule
`default_nettype wire

View File

@@ -0,0 +1,45 @@
/*
* servile_arbiter.v : I/D arbiter for the servile convenience wrapper.
* Relies on the fact that not ibus and dbus are active at the same time.
*
* SPDX-FileCopyrightText: 2024 Olof Kindgren <olof.kindgren@gmail.com>
* SPDX-License-Identifier: Apache-2.0
*/
module servile_arbiter
(
input wire [31:0] i_wb_cpu_dbus_adr,
input wire [31:0] i_wb_cpu_dbus_dat,
input wire [3:0] i_wb_cpu_dbus_sel,
input wire i_wb_cpu_dbus_we,
input wire i_wb_cpu_dbus_stb,
output wire [31:0] o_wb_cpu_dbus_rdt,
output wire o_wb_cpu_dbus_ack,
input wire [31:0] i_wb_cpu_ibus_adr,
input wire i_wb_cpu_ibus_stb,
output wire [31:0] o_wb_cpu_ibus_rdt,
output wire o_wb_cpu_ibus_ack,
output wire [31:0] o_wb_mem_adr,
output wire [31:0] o_wb_mem_dat,
output wire [3:0] o_wb_mem_sel,
output wire o_wb_mem_we,
output wire o_wb_mem_stb,
input wire [31:0] i_wb_mem_rdt,
input wire i_wb_mem_ack);
assign o_wb_cpu_dbus_rdt = i_wb_mem_rdt;
assign o_wb_cpu_dbus_ack = i_wb_mem_ack & !i_wb_cpu_ibus_stb;
assign o_wb_cpu_ibus_rdt = i_wb_mem_rdt;
assign o_wb_cpu_ibus_ack = i_wb_mem_ack & i_wb_cpu_ibus_stb;
assign o_wb_mem_adr = i_wb_cpu_ibus_stb ? i_wb_cpu_ibus_adr : i_wb_cpu_dbus_adr;
assign o_wb_mem_dat = i_wb_cpu_dbus_dat;
assign o_wb_mem_sel = i_wb_cpu_dbus_sel;
assign o_wb_mem_we = i_wb_cpu_dbus_we & !i_wb_cpu_ibus_stb;
assign o_wb_mem_stb = i_wb_cpu_ibus_stb | i_wb_cpu_dbus_stb;
endmodule

100
rtl/qerv/servile_mux.v Normal file
View File

@@ -0,0 +1,100 @@
/*
* servile_mux.v : Simple Wishbone mux for the servile convenience wrapper.
*
* SPDX-FileCopyrightText: 2024 Olof Kindgren <olof.kindgren@gmail.com>
* SPDX-License-Identifier: Apache-2.0
*/
module servile_mux
#(parameter [0:0] sim = 1'b0, //Enable simulation features
parameter [31:0] sim_sig_adr = 32'h80000000,
parameter [31:0] sim_halt_adr = 32'h90000000)
(
input wire i_clk,
input wire i_rst,
input wire [31:0] i_wb_cpu_adr,
input wire [31:0] i_wb_cpu_dat,
input wire [3:0] i_wb_cpu_sel,
input wire i_wb_cpu_we,
input wire i_wb_cpu_stb,
output wire [31:0] o_wb_cpu_rdt,
output wire o_wb_cpu_ack,
output wire [31:0] o_wb_mem_adr,
output wire [31:0] o_wb_mem_dat,
output wire [3:0] o_wb_mem_sel,
output wire o_wb_mem_we,
output wire o_wb_mem_stb,
input wire [31:0] i_wb_mem_rdt,
input wire i_wb_mem_ack,
output wire [31:0] o_wb_ext_adr,
output wire [31:0] o_wb_ext_dat,
output wire [3:0] o_wb_ext_sel,
output wire o_wb_ext_we,
output wire o_wb_ext_stb,
input wire [31:0] i_wb_ext_rdt,
input wire i_wb_ext_ack);
wire sig_en;
wire halt_en;
reg sim_ack;
wire ext = (i_wb_cpu_adr[31:30] != 2'b00);
assign o_wb_cpu_rdt = ext ? i_wb_ext_rdt : i_wb_mem_rdt;
assign o_wb_cpu_ack = i_wb_ext_ack | i_wb_mem_ack | sim_ack;
assign o_wb_mem_adr = i_wb_cpu_adr;
assign o_wb_mem_dat = i_wb_cpu_dat;
assign o_wb_mem_sel = i_wb_cpu_sel;
assign o_wb_mem_we = i_wb_cpu_we;
assign o_wb_mem_stb = i_wb_cpu_stb & !ext & !(sig_en|halt_en);
assign o_wb_ext_adr = i_wb_cpu_adr;
assign o_wb_ext_dat = i_wb_cpu_dat;
assign o_wb_ext_sel = i_wb_cpu_sel;
assign o_wb_ext_we = i_wb_cpu_we;
assign o_wb_ext_stb = i_wb_cpu_stb & ext & !(sig_en|halt_en);
generate
if (sim) begin
integer f = 0;
assign sig_en = |f & i_wb_cpu_we & (i_wb_cpu_adr == sim_sig_adr);
assign halt_en = i_wb_cpu_we & (i_wb_cpu_adr == sim_halt_adr);
reg [1023:0] signature_file;
initial
/* verilator lint_off WIDTH */
if ($value$plusargs("signature=%s", signature_file)) begin
$display("Writing signature to %0s", signature_file);
f = $fopen(signature_file, "w");
end
/* verilator lint_on WIDTH */
always @(posedge i_clk) begin
sim_ack <= 1'b0;
if (i_wb_cpu_stb & !sim_ack) begin
sim_ack <= sig_en|halt_en;
if (sig_en & (f != 0))
$fwrite(f, "%c", i_wb_cpu_dat[7:0]);
else if(halt_en) begin
$display("Test complete");
$finish;
end
end
if (i_rst)
sim_ack <= 1'b0;
end
end else begin
assign sig_en = 1'b0;
assign halt_en = 1'b0;
initial sim_ack = 1'b0;
end
endgenerate
endmodule

View File

@@ -0,0 +1,77 @@
/*
* servile_rf_mem_if.v : Arbiter to allow a shared SRAM for RF and memory accesses. RF is mapped to the highest 128 bytes of the memory. Requires 8-bit RF accesses.
*
* SPDX-FileCopyrightText: 2024 Olof Kindgren <olof.kindgren@gmail.com>
* SPDX-License-Identifier: Apache-2.0
*/
`default_nettype none
module servile_rf_mem_if
#(//Memory parameters
parameter depth = 256,
//RF parameters
parameter rf_regs = 32,
//Internally calculated. Do not touch
parameter rf_depth = $clog2(rf_regs*4),
parameter aw = $clog2(depth))
(
input wire i_clk,
input wire i_rst,
input wire [rf_depth-1:0] i_waddr,
input wire [7:0] i_wdata,
input wire i_wen,
input wire [rf_depth-1:0] i_raddr,
output wire [7:0] o_rdata,
input wire i_ren,
output wire [aw-1:0] o_sram_waddr,
output wire [7:0] o_sram_wdata,
output wire o_sram_wen,
output wire [aw-1:0] o_sram_raddr,
input wire [7:0] i_sram_rdata,
output wire o_sram_ren,
input wire [aw-1:2] i_wb_adr,
input wire [31:0] i_wb_dat,
input wire [3:0] i_wb_sel,
input wire i_wb_we,
input wire i_wb_stb,
output wire [31:0] o_wb_rdt,
output reg o_wb_ack);
reg [1:0] bsel;
wire wb_en = i_wb_stb & !i_wen & !o_wb_ack;
wire wb_we = i_wb_we & i_wb_sel[bsel];
wire [aw-1:0] rf_waddr = ~{{aw-rf_depth{1'b0}},i_waddr};
wire [aw-1:0] rf_raddr = ~{{aw-rf_depth{1'b0}},i_raddr};
assign o_sram_waddr = wb_en ? {i_wb_adr[aw-1:2],bsel} : rf_waddr;
assign o_sram_wdata = wb_en ? i_wb_dat[bsel*8+:8] : i_wdata;
assign o_sram_wen = wb_en ? wb_we : i_wen;
assign o_sram_raddr = wb_en ? {i_wb_adr[aw-1:2],bsel} : rf_raddr;
assign o_sram_ren = wb_en ? !i_wb_we : i_ren;
reg [23:0] wb_rdt;
assign o_wb_rdt = {i_sram_rdata, wb_rdt};
reg regzero;
always @(posedge i_clk) begin
if (wb_en) bsel <= bsel + 2'd1;
o_wb_ack <= wb_en & &bsel;
if (bsel == 2'b01) wb_rdt[7:0] <= i_sram_rdata;
if (bsel == 2'b10) wb_rdt[15:8] <= i_sram_rdata;
if (bsel == 2'b11) wb_rdt[23:16] <= i_sram_rdata;
if (i_rst) begin
bsel <= 2'd0;
o_wb_ack <= 1'b0;
end
regzero <= &i_raddr[rf_depth-1:2];
end
assign o_rdata = regzero ? 8'd0 : i_sram_rdata;
endmodule

View File

@@ -1,6 +1,8 @@
`timescale 1ns/1ps
module top_generic(
module top_generic #(
parameter sim = 0
)(
input wire aclk,
input wire aresetn,
@@ -11,9 +13,6 @@ module top_generic(
output wire[7:0] LED
);
`include "conv.vh"
assign led_green = 1'b0;
assign led_red = 1'b0;
assign LED = 8'h00;
// Clocking
wire clk_100;
@@ -29,8 +28,11 @@ module top_generic(
wire [31:0] GPIO_C;
wire [31:0] GPIO_D;
wire test;
mcu #(
.memfile("../sw/sweep/sweep.hex")
.memfile("../sw/sweep/sweep.hex"),
.sim(sim)
) mcu (
.i_clk(clk_15),
.i_rst(!aresetn),
@@ -41,7 +43,8 @@ module top_generic(
.o_GPO_A(GPIO_A),
.o_GPO_B(GPIO_B),
.o_GPO_C(GPIO_C),
.o_GPO_D(GPIO_D)
.o_GPO_D(GPIO_D),
.o_test(test)
);
@@ -64,4 +67,8 @@ module top_generic(
dac_code <= q15_to_uq16(sin_q15) >> 10;
end
assign r2r = dac_code;
assign LED = GPIO_B[7:0];
assign led_green = GPIO_C[0];
assign led_red = test;
endmodule

145
rtl/wb/wb_mux.v Normal file
View File

@@ -0,0 +1,145 @@
/* wb_mux. Part of wb_intercon
*
* ISC License
*
* Copyright (C) 2013-2019 Olof Kindgren <olof.kindgren@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Wishbone multiplexer, burst-compatible
Simple mux with an arbitrary number of slaves.
The parameters MATCH_ADDR and MATCH_MASK are flattened arrays
aw*NUM_SLAVES sized arrays that are used to calculate the
active slave. slave i is selected when
(wb_adr_i & MATCH_MASK[(i+1)*aw-1:i*aw] is equal to
MATCH_ADDR[(i+1)*aw-1:i*aw]
If several regions are overlapping, the slave with the lowest
index is selected. This can be used to have fallback
functionality in the last slave, in case no other slave was
selected.
If no match is found, the wishbone transaction will stall and
an external watchdog is required to abort the transaction
Todo:
Registered master/slave connections
Rewrite with System Verilog 2D arrays when tools support them
*/
`include "../util/clog2.vh"
module wb_mux
#(parameter dw = 32, // Data width
parameter aw = 32, // Address width
parameter num_devices = 2, // Number of devices
parameter num_slaves = num_devices, // Number of devices (deprecated)
parameter [num_slaves*aw-1:0] MATCH_ADDR = 0,
parameter [num_slaves*aw-1:0] MATCH_MASK = 0)
(
input wire wb_clk_i,
input wire wb_rst_i,
// Master Interface
input wire [aw-1:0] wbm_adr_i,
input wire [dw-1:0] wbm_dat_i,
input wire [3:0] wbm_sel_i,
input wire wbm_we_i,
input wire wbm_cyc_i,
input wire wbm_stb_i,
input wire [2:0] wbm_cti_i,
input wire [1:0] wbm_bte_i,
output wire [dw-1:0] wbm_dat_o,
output wire wbm_ack_o,
output wire wbm_err_o,
output wire wbm_rty_o,
// Wishbone Slave interface
output wire [num_slaves*aw-1:0] wbs_adr_o,
output wire [num_slaves*dw-1:0] wbs_dat_o,
output wire [num_slaves*4-1:0] wbs_sel_o,
output wire [num_slaves-1:0] wbs_we_o,
output wire [num_slaves-1:0] wbs_cyc_o,
output wire [num_slaves-1:0] wbs_stb_o,
output wire [num_slaves*3-1:0] wbs_cti_o,
output wire [num_slaves*2-1:0] wbs_bte_o,
input wire [num_slaves*dw-1:0] wbs_dat_i,
input wire [num_slaves-1:0] wbs_ack_i,
input wire [num_slaves-1:0] wbs_err_i,
input wire [num_slaves-1:0] wbs_rty_i);
///////////////////////////////////////////////////////////////////////////////
// Master/slave connection
///////////////////////////////////////////////////////////////////////////////
//Use parameter instead of localparam to work around a bug in Xilinx ISE
parameter slave_sel_bits = num_slaves > 1 ? `CLOG2(num_slaves) : 1;
reg wbm_err;
wire [slave_sel_bits-1:0] slave_sel;
wire [num_slaves-1:0] match;
genvar idx;
generate
for(idx=0; idx<num_slaves ; idx=idx+1) begin : addr_match
assign match[idx] = (wbm_adr_i & MATCH_MASK[idx*aw+:aw]) == MATCH_ADDR[idx*aw+:aw];
end
endgenerate
//
// Find First 1 - Start from MSB and count downwards, returns 0 when no bit set
//
function [slave_sel_bits-1:0] ff1;
input [num_slaves-1:0] in;
integer i;
begin
ff1 = 0;
for (i = num_slaves-1; i >= 0; i=i-1) begin
if (in[i])
/* verilator lint_off WIDTH */
ff1 = i;
/* verilator lint_on WIDTH */
end
end
endfunction
assign slave_sel = ff1(match);
always @(posedge wb_clk_i)
wbm_err <= wbm_cyc_i & !(|match);
assign wbs_adr_o = {num_slaves{wbm_adr_i}};
assign wbs_dat_o = {num_slaves{wbm_dat_i}};
assign wbs_sel_o = {num_slaves{wbm_sel_i}};
assign wbs_we_o = {num_slaves{wbm_we_i}};
/* verilator lint_off WIDTH */
// Expand master CYC to slave bus width before shifting to one-hot select.
// Shifting a 1-bit signal would otherwise zero out all but slave 0.
assign wbs_cyc_o = match & ({num_slaves{wbm_cyc_i}} << slave_sel);
/* verilator lint_on WIDTH */
assign wbs_stb_o = {num_slaves{wbm_stb_i}};
assign wbs_cti_o = {num_slaves{wbm_cti_i}};
assign wbs_bte_o = {num_slaves{wbm_bte_i}};
assign wbm_dat_o = wbs_dat_i[slave_sel*dw+:dw];
assign wbm_ack_o = wbs_ack_i[slave_sel];
assign wbm_err_o = wbs_err_i[slave_sel] | wbm_err;
assign wbm_rty_o = wbs_rty_i[slave_sel];
endmodule

74
rtl/wb/wb_timer.v Normal file
View File

@@ -0,0 +1,74 @@
`timescale 1ns/1ps
module wb_countdown_timer #(
parameter WIDTH = 32, // counter width (<=32 makes bus mapping easy)
parameter DIVIDER = 0 // optional prescaler: tick every 2^DIVIDER cycles
)(
input wire i_clk,
input wire i_rst,
output reg o_irq,
input wire [31:0] i_wb_dat,
output reg [31:0] o_wb_dat,
input wire i_wb_we,
input wire i_wb_cyc,
input wire i_wb_stb,
output wire o_wb_ack
);
// One-cycle acknowledge on any valid WB access
// (classic, zero-wait-state peripheral)
assign o_wb_ack = i_wb_cyc & i_wb_stb;
// Internal countdown and prescaler
reg [WIDTH-1:0] counter;
reg [DIVIDER:0] presc; // enough bits to count up to 2^DIVIDER-1
wire tick = (DIVIDER == 0) ? 1'b1 : (presc[DIVIDER] == 1'b1);
// Readback: expose the current counter value
always @(*) begin
o_wb_dat = 32'd0;
o_wb_dat[WIDTH-1:0] = counter;
end
// Main logic
always @(posedge i_clk) begin
if (i_rst) begin
counter <= {WIDTH{1'b0}};
presc <= { (DIVIDER+1){1'b0} };
o_irq <= 1'b0;
end else begin
// Default prescaler behavior
if (DIVIDER != 0) begin
if (counter != 0 && !o_irq)
presc <= presc + 1'b1;
else
presc <= { (DIVIDER+1){1'b0} };
end
// Wishbone write: load counter and clear IRQ
if (o_wb_ack && i_wb_we) begin
counter <= i_wb_dat[WIDTH-1:0];
o_irq <= 1'b0;
// reset prescaler on (re)start or stop
presc <= { (DIVIDER+1){1'b0} };
end else begin
// Countdown when running (counter>0), not already IRQ'd
if (!o_irq && counter != 0) begin
if (tick) begin
if (counter == 1) begin
counter <= {WIDTH{1'b0}};
o_irq <= 1'b1; // sticky until next write
presc <= { (DIVIDER+1){1'b0} };
end else begin
counter <= counter - 1'b1;
end
end
end
end
end
end
endmodule

45
sim/tb/tb_top_generic.v Normal file
View File

@@ -0,0 +1,45 @@
`timescale 1ns/1ps
module tb_top_generic();
reg aclk;
reg aresetn;
wire led_green;
wire led_red;
wire [5:0] r2r;
wire [7:0] LED;
// 100 MHz board input clock
initial aclk = 1'b0;
always #5 aclk = ~aclk;
// Hold reset low, then release
initial begin
aresetn = 1'b0;
#200;
aresetn = 1'b1;
end
top_generic #(
.sim(1)
) dut (
.aclk(aclk),
.aresetn(aresetn),
.led_green(led_green),
.led_red(led_red),
.r2r(r2r),
.LED(LED)
);
// Ensure firmware path resolves from repository root when simulating.
defparam dut.mcu.memfile = "sw/sweep/sweep.hex";
initial begin
$dumpfile("out.vcd");
$dumpvars(0, tb_top_generic);
// Let firmware run for a while.
#5_000_000;
$finish;
end
endmodule

157
sim/tb/tb_wb_timer.v Normal file
View File

@@ -0,0 +1,157 @@
`timescale 1ns/1ps
module tb_wb_timer();
localparam integer CLK_HALF_NS = 5;
reg clk;
reg rst;
reg [31:0] i_wb_dat;
wire [31:0] o_wb_dat;
reg i_wb_we;
reg i_wb_cyc;
reg i_wb_stb;
wire o_wb_ack;
wire o_irq;
integer errors;
wb_countdown_timer #(
.WIDTH(8),
.DIVIDER(0)
) dut (
.i_clk (clk),
.i_rst (rst),
.o_irq (o_irq),
.i_wb_dat(i_wb_dat),
.o_wb_dat(o_wb_dat),
.i_wb_we (i_wb_we),
.i_wb_cyc(i_wb_cyc),
.i_wb_stb(i_wb_stb),
.o_wb_ack(o_wb_ack)
);
always #CLK_HALF_NS clk = ~clk;
task wb_write;
input [31:0] data;
begin
@(negedge clk);
i_wb_dat = data;
i_wb_we = 1'b1;
i_wb_cyc = 1'b1;
i_wb_stb = 1'b1;
#1;
if (!o_wb_ack) begin
$display("FAIL: WB write ACK not asserted when cyc/stb high");
errors = errors + 1;
end
@(negedge clk);
i_wb_we = 1'b0;
i_wb_cyc = 1'b0;
i_wb_stb = 1'b0;
end
endtask
task wb_read_check;
input [31:0] expected;
begin
@(negedge clk);
i_wb_we = 1'b0;
i_wb_cyc = 1'b1;
i_wb_stb = 1'b1;
#1;
if (!o_wb_ack) begin
$display("FAIL: WB read ACK not asserted when cyc/stb high");
errors = errors + 1;
end
if (o_wb_dat !== expected) begin
$display("FAIL: WB read mismatch. got=0x%08h expected=0x%08h", o_wb_dat, expected);
errors = errors + 1;
end
@(negedge clk);
i_wb_cyc = 1'b0;
i_wb_stb = 1'b0;
end
endtask
task wait_irq_with_timeout;
input integer max_cycles;
integer k;
begin
k = 0;
while ((o_irq !== 1'b1) && (k < max_cycles)) begin
@(posedge clk);
k = k + 1;
end
if (o_irq !== 1'b1) begin
$display("FAIL: IRQ did not assert within %0d cycles", max_cycles);
errors = errors + 1;
end
end
endtask
initial begin
$dumpfile("out.vcd");
$dumpvars;
clk = 1'b0;
rst = 1'b1;
i_wb_dat = 32'd0;
i_wb_we = 1'b0;
i_wb_cyc = 1'b0;
i_wb_stb = 1'b0;
errors = 0;
$display("---------------------------------------");
$display("Testbench: wb_countdown_timer");
$display("---------------------------------------");
repeat (3) @(posedge clk);
rst = 1'b0;
repeat (1) @(posedge clk);
if (o_irq !== 1'b0) begin
$display("FAIL: IRQ should be low after reset");
errors = errors + 1;
end
wb_read_check(32'd0);
// Load timer and check readback at a deterministic point
wb_write(32'd5);
@(posedge clk);
wb_read_check(32'd4);
// Wait until expiration, then verify sticky IRQ behavior
wait_irq_with_timeout(6);
wb_read_check(32'd0);
repeat (2) @(posedge clk);
if (o_irq !== 1'b1) begin
$display("FAIL: IRQ should remain sticky until next write");
errors = errors + 1;
end
// A new write must clear IRQ and restart the counter
wb_write(32'd2);
if (o_irq !== 1'b0) begin
$display("FAIL: IRQ should clear on write");
errors = errors + 1;
end
wait_irq_with_timeout(3);
if (errors == 0)
$display("PASS: wb_timer basic functionality verified");
else
$display("FAIL: wb_timer testbench found %0d error(s)", errors);
$finish;
end
endmodule

View File

@@ -3,7 +3,7 @@ ENTRY(_start)
MEMORY
{
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8192
}
SECTIONS
@@ -29,5 +29,5 @@ SECTIONS
} > RAM
. = ALIGN(4);
__stack_top = ORIGIN(RAM) + LENGTH(RAM);
__stack_top = ORIGIN(RAM) + LENGTH(RAM) - 256;
}

View File

@@ -8,7 +8,7 @@ void main(){
for(;;){
for(int i=1000; i<10000; i++){
*R_FREQ = i;
for(int j=0; j<100; j++) asm volatile("nop");
// for(int j=0; j<100; j++) asm volatile("nop");
}
}
}