Compare commits
5 Commits
jtagram
...
3a3c951409
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a3c951409 | |||
| f2f9644830 | |||
| 13f72e698f | |||
| 9930ce4461 | |||
| 8f4e887b9d |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
out
|
||||
build
|
||||
env
|
||||
env
|
||||
__pycache*
|
||||
_impactbatch.log
|
||||
@@ -24,4 +24,29 @@ NET "r2r[1]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[2]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[3]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[4]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[5]" IOSTANDARD = LVCMOS33;
|
||||
NET "r2r[5]" IOSTANDARD = LVCMOS33;
|
||||
|
||||
NET "LED[0]" LOC = P119;
|
||||
NET "LED[0]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[0]" DRIVE = 8;
|
||||
NET "LED[1]" LOC = P118;
|
||||
NET "LED[1]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[1]" DRIVE = 8;
|
||||
NET "LED[2]" LOC = P117;
|
||||
NET "LED[2]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[2]" DRIVE = 8;
|
||||
NET "LED[3]" LOC = P116;
|
||||
NET "LED[3]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[3]" DRIVE = 8;
|
||||
NET "LED[4]" LOC = P115;
|
||||
NET "LED[4]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[4]" DRIVE = 8;
|
||||
NET "LED[5]" LOC = P114;
|
||||
NET "LED[5]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[5]" DRIVE = 8;
|
||||
NET "LED[6]" LOC = P112;
|
||||
NET "LED[6]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[6]" DRIVE = 8;
|
||||
NET "LED[7]" LOC = P111;
|
||||
NET "LED[7]" IOSTANDARD = LVCMOS33;
|
||||
NET "LED[7]" DRIVE = 8;
|
||||
161
project.cfg
161
project.cfg
@@ -4,6 +4,15 @@ version = 0.1
|
||||
out_dir = out
|
||||
build_dir = build
|
||||
|
||||
[target.ip]
|
||||
toolchain = ISE_IP
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
family = spartan6
|
||||
device = xc6slx9
|
||||
package = tqg144
|
||||
speedgrade = -2
|
||||
files_def = boards/mimas_v1/ip/clk_gen.xco
|
||||
|
||||
[target.synth]
|
||||
toolchain = ISE
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
@@ -13,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
|
||||
@@ -22,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
|
||||
@@ -38,49 +53,145 @@ 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/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/core/soclet.v
|
||||
rtl/wb/wb_mux.v
|
||||
rtl/wb/jtag_wb_bridge.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.ip]
|
||||
toolchain = ISE_IP
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
family = spartan6
|
||||
device = xc6slx9
|
||||
package = tqg144
|
||||
speedgrade = -2
|
||||
files_def = boards/mimas_v1/ip/clk_gen.xco
|
||||
|
||||
[target.sim]
|
||||
[target.synth_sim]
|
||||
toolchain = iverilog
|
||||
runtime = all
|
||||
toplevel = tb_sigmadelta
|
||||
toplevel = tb_top_generic
|
||||
ivl_opts = -Irtl/util
|
||||
files_verilog = sim/tb/tb_nco_q15.v
|
||||
sim/tb/tb_sigmadelta.v
|
||||
sim/tb/tb_mul_const.v
|
||||
files_verilog = rtl/toplevel/top_generic.v
|
||||
rtl/util/conv.vh
|
||||
rtl/core/nco_q15.v
|
||||
rtl/core/lvds_comparator.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
|
||||
sim/overrides/sigmadelta_sampler.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
|
||||
files_other = rtl/util/conv.vh
|
||||
rtl/util/rc_alpha_q15.vh
|
||||
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
|
||||
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
|
||||
family = spartan6
|
||||
device = xc6slx9
|
||||
package = tqg144
|
||||
speedgrade = -2
|
||||
toplevel = top_jtag
|
||||
xst_opts = -vlgincdir rtl/util -keep_hierarchy yes
|
||||
files_other =
|
||||
files_con = boards/mimas_v1/constraints.ucf
|
||||
files_verilog = rtl/arch/spartan-6/jtag_if.v
|
||||
rtl/arch/spartan-6/clk_gen.v
|
||||
rtl/wb/jtag_wb_bridge.v
|
||||
rtl/wb/wb_gpio.v
|
||||
rtl/toplevel/top_jtag.v
|
||||
|
||||
[target.svftest]
|
||||
toolchain = iverilog
|
||||
runtime = all
|
||||
toplevel = tb_svf
|
||||
files_verilog = sim/tb/tb_svf.v
|
||||
sim/overrides/jtag_if.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
|
||||
buildroot = tools
|
||||
files_makefile = tools/Makefile
|
||||
files_other = tools/digilent_jtag.cpp
|
||||
tools/digilent_jtag.hpp
|
||||
tools/argparse.cpp
|
||||
tools/argparse.hpp
|
||||
tools/test.cpp
|
||||
|
||||
35
rtl/arch/spartan-6/jtag_if.v
Normal file
35
rtl/arch/spartan-6/jtag_if.v
Normal file
@@ -0,0 +1,35 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// =============================================================================
|
||||
// JTAG interface
|
||||
// Spartan-6 BSCAN primitive wrapper (USER1 chain).
|
||||
// =============================================================================
|
||||
module jtag_if #(
|
||||
parameter chain = 1
|
||||
)(
|
||||
input wire i_tdo,
|
||||
output wire o_tck,
|
||||
output wire o_tdi,
|
||||
output wire o_drck,
|
||||
output wire o_capture,
|
||||
output wire o_shift,
|
||||
output wire o_update,
|
||||
output wire o_runtest,
|
||||
output wire o_reset,
|
||||
output wire o_sel
|
||||
);
|
||||
BSCAN_SPARTAN6 #(
|
||||
.JTAG_CHAIN(chain)
|
||||
) bscan_i (
|
||||
.CAPTURE(o_capture),
|
||||
.DRCK(o_drck),
|
||||
.RESET(o_reset),
|
||||
.RUNTEST(o_runtest),
|
||||
.SEL(o_sel),
|
||||
.SHIFT(o_shift),
|
||||
.TCK(o_tck),
|
||||
.TDI(o_tdi),
|
||||
.TDO(i_tdo),
|
||||
.UPDATE(o_update)
|
||||
);
|
||||
endmodule
|
||||
@@ -1,49 +0,0 @@
|
||||
`default_nettype none
|
||||
|
||||
// Spartan-6 JTAG TAP wrapper with an architecture-neutral interface.
|
||||
// Re-implement this module for other FPGA families with the same port list.
|
||||
module jtag_tap_spartan6
|
||||
#(parameter USER_CHAIN = 1)
|
||||
(
|
||||
output wire o_drck,
|
||||
output wire o_capture,
|
||||
output wire o_shift,
|
||||
output wire o_update,
|
||||
output wire o_reset,
|
||||
output wire o_sel,
|
||||
output wire o_tdi,
|
||||
input wire i_tdo
|
||||
);
|
||||
|
||||
wire drck1;
|
||||
wire drck2;
|
||||
wire sel1;
|
||||
wire sel2;
|
||||
wire tdo1;
|
||||
wire tdo2;
|
||||
|
||||
localparam USE_CHAIN2 = (USER_CHAIN == 2);
|
||||
|
||||
assign o_drck = USE_CHAIN2 ? drck2 : drck1;
|
||||
assign o_sel = USE_CHAIN2 ? sel2 : sel1;
|
||||
assign tdo1 = USE_CHAIN2 ? 1'b0 : i_tdo;
|
||||
assign tdo2 = USE_CHAIN2 ? i_tdo : 1'b0;
|
||||
|
||||
BSCAN_SPARTAN6
|
||||
#(.JTAG_CHAIN(USER_CHAIN))
|
||||
bscan_spartan6
|
||||
(
|
||||
.CAPTURE(o_capture),
|
||||
.DRCK1(drck1),
|
||||
.DRCK2(drck2),
|
||||
.RESET(o_reset),
|
||||
.SEL1(sel1),
|
||||
.SEL2(sel2),
|
||||
.SHIFT(o_shift),
|
||||
.TDI(o_tdi),
|
||||
.UPDATE(o_update),
|
||||
.TDO1(tdo1),
|
||||
.TDO2(tdo2)
|
||||
);
|
||||
|
||||
endmodule
|
||||
34
rtl/core/jtag_if.v
Normal file
34
rtl/core/jtag_if.v
Normal file
@@ -0,0 +1,34 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// =============================================================================
|
||||
// JTAG interface
|
||||
// Generic stub model with inactive/tied-off outputs.
|
||||
// =============================================================================
|
||||
module jtag_if #(
|
||||
parameter chain = 1
|
||||
)(
|
||||
input wire i_tdo,
|
||||
output wire o_tck,
|
||||
output wire o_tdi,
|
||||
output wire o_drck,
|
||||
output wire o_capture,
|
||||
output wire o_shift,
|
||||
output wire o_update,
|
||||
output wire o_runtest,
|
||||
output wire o_reset,
|
||||
output wire o_sel
|
||||
);
|
||||
assign o_tck = 1'b0;
|
||||
assign o_tdi = 1'b0;
|
||||
assign o_drck = 1'b0;
|
||||
assign o_capture = 1'b0;
|
||||
assign o_shift = 1'b0;
|
||||
assign o_update = 1'b0;
|
||||
assign o_runtest = 1'b0;
|
||||
assign o_reset = 1'b0;
|
||||
assign o_sel = 1'b0;
|
||||
|
||||
// Keep lint tools quiet in generic builds where TDO is unused.
|
||||
wire _unused_tdo;
|
||||
assign _unused_tdo = i_tdo;
|
||||
endmodule
|
||||
183
rtl/core/mcu.v
Normal file
183
rtl/core/mcu.v
Normal file
@@ -0,0 +1,183 @@
|
||||
`timescale 1ns/1ps
|
||||
`include "../util/clog2.vh"
|
||||
|
||||
module mcu #(
|
||||
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,
|
||||
output wire o_test
|
||||
);
|
||||
localparam WITH_CSR = 1;
|
||||
localparam regs = 32+WITH_CSR*4;
|
||||
localparam rf_width = 8;
|
||||
|
||||
wire rst;
|
||||
wire rst_mem_reason;
|
||||
wire timer_irq;
|
||||
assign rst = i_rst | rst_mem_reason;
|
||||
|
||||
assign o_test = timer_irq;
|
||||
|
||||
// Busses
|
||||
// CPU->memory
|
||||
wire [31:0] wb_mem_adr;
|
||||
wire [31:0] wb_mem_dat;
|
||||
wire [3:0] wb_mem_sel;
|
||||
wire wb_mem_we;
|
||||
wire wb_mem_stb;
|
||||
wire [31:0] wb_mem_rdt;
|
||||
wire wb_mem_ack;
|
||||
// CPU->peripherals
|
||||
wire [31:0] wb_ext_adr;
|
||||
wire [31:0] wb_ext_dat;
|
||||
wire [3:0] wb_ext_sel;
|
||||
wire wb_ext_we;
|
||||
wire wb_ext_stb;
|
||||
wire [31:0] wb_ext_rdt;
|
||||
wire wb_ext_ack;
|
||||
// CPU->RF
|
||||
wire [6+WITH_CSR:0] rf_waddr;
|
||||
wire [rf_width-1:0] rf_wdata;
|
||||
wire rf_wen;
|
||||
wire [6+WITH_CSR:0] rf_raddr;
|
||||
wire [rf_width-1:0] rf_rdata;
|
||||
wire rf_ren;
|
||||
// combined RF and mem bus to actual RAM
|
||||
wire [`CLOG2(memsize)-1:0] sram_waddr;
|
||||
wire [rf_width-1:0] sram_wdata;
|
||||
wire sram_wen;
|
||||
wire [`CLOG2(memsize)-1:0] sram_raddr;
|
||||
wire [rf_width-1:0] sram_rdata;
|
||||
wire sram_ren;
|
||||
|
||||
// GPIO
|
||||
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;
|
||||
|
||||
// SERV core with mux splitting dbus into mem and ext and
|
||||
// arbiter combining mem and ibus
|
||||
// separate rst line to let other hardware keep core under reset
|
||||
servile #(
|
||||
.reset_pc(32'h0000_0000),
|
||||
.reset_strategy("MINI"),
|
||||
.rf_width(rf_width),
|
||||
.sim(sim),
|
||||
.with_csr(WITH_CSR),
|
||||
.with_c(0),
|
||||
.with_mdu(0)
|
||||
) servile (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(rst),
|
||||
.i_timer_irq(1'b0), //timer_irq),
|
||||
|
||||
//Memory interface
|
||||
.o_wb_mem_adr(wb_mem_adr),
|
||||
.o_wb_mem_dat(wb_mem_dat),
|
||||
.o_wb_mem_sel(wb_mem_sel),
|
||||
.o_wb_mem_we(wb_mem_we),
|
||||
.o_wb_mem_stb(wb_mem_stb),
|
||||
.i_wb_mem_rdt(wb_mem_rdt),
|
||||
.i_wb_mem_ack(wb_mem_ack),
|
||||
|
||||
//Extension interface
|
||||
.o_wb_ext_adr(wb_ext_adr),
|
||||
.o_wb_ext_dat(wb_ext_dat),
|
||||
.o_wb_ext_sel(wb_ext_sel),
|
||||
.o_wb_ext_we(wb_ext_we),
|
||||
.o_wb_ext_stb(wb_ext_stb),
|
||||
.i_wb_ext_rdt(wb_ext_rdt),
|
||||
.i_wb_ext_ack(wb_ext_ack),
|
||||
|
||||
//RF IF
|
||||
.o_rf_waddr(rf_waddr),
|
||||
.o_rf_wdata(rf_wdata),
|
||||
.o_rf_wen(rf_wen),
|
||||
.o_rf_raddr(rf_raddr),
|
||||
.o_rf_ren(rf_ren),
|
||||
.i_rf_rdata(rf_rdata)
|
||||
);
|
||||
|
||||
// WB arbiter combining RF and mem interfaces into 1
|
||||
// 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 (rst),
|
||||
|
||||
.i_waddr(rf_waddr),
|
||||
.i_wdata(rf_wdata),
|
||||
.i_wen(rf_wen),
|
||||
.i_raddr(rf_raddr),
|
||||
.o_rdata(rf_rdata),
|
||||
.i_ren(rf_ren),
|
||||
|
||||
.o_sram_waddr(sram_waddr),
|
||||
.o_sram_wdata(sram_wdata),
|
||||
.o_sram_wen(sram_wen),
|
||||
.o_sram_raddr(sram_raddr),
|
||||
.i_sram_rdata(sram_rdata),
|
||||
// .o_sram_ren(sram_ren),
|
||||
|
||||
.i_wb_adr(wb_mem_adr[`CLOG2(memsize)-1:2]),
|
||||
.i_wb_stb(wb_mem_stb),
|
||||
.i_wb_we(wb_mem_we) ,
|
||||
.i_wb_sel(wb_mem_sel),
|
||||
.i_wb_dat(wb_mem_dat),
|
||||
.o_wb_rdt(wb_mem_rdt),
|
||||
.o_wb_ack(wb_mem_ack)
|
||||
);
|
||||
|
||||
memory #(
|
||||
.memfile(memfile),
|
||||
.depth(memsize),
|
||||
.sim(sim)
|
||||
) mem (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_waddr(sram_waddr),
|
||||
.i_wdata(sram_wdata),
|
||||
.i_wen(sram_wen),
|
||||
.i_raddr(sram_raddr),
|
||||
.o_rdata(sram_rdata),
|
||||
.o_core_reset(rst_mem_reason)
|
||||
);
|
||||
|
||||
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),
|
||||
.o_wb_rdt(wb_ext_rdt),
|
||||
.o_wb_ack(wb_ext_ack),
|
||||
// Peripheral IO
|
||||
.i_gpio(GPI),
|
||||
.o_gpio(GPO),
|
||||
.o_timer_irq(timer_irq)
|
||||
);
|
||||
|
||||
endmodule
|
||||
123
rtl/core/mcu_peripherals.v
Normal file
123
rtl/core/mcu_peripherals.v
Normal 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
|
||||
91
rtl/core/mem_jtag_writable.v
Normal file
91
rtl/core/mem_jtag_writable.v
Normal 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
|
||||
@@ -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
7
rtl/qerv/LICENSE
Normal 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
152
rtl/qerv/qerv_immdec.v
Normal 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
222
rtl/qerv/serv_rf_top.v
Normal 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
|
||||
136
rtl/qerv/serv_synth_wrapper.v
Normal file
136
rtl/qerv/serv_synth_wrapper.v
Normal 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
694
rtl/qerv/serv_top.v
Normal 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
279
rtl/qerv/servile.v
Normal 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
|
||||
45
rtl/qerv/servile_arbiter.v
Normal file
45
rtl/qerv/servile_arbiter.v
Normal 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
100
rtl/qerv/servile_mux.v
Normal 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
|
||||
77
rtl/qerv/servile_rf_mem_if.v
Normal file
77
rtl/qerv/servile_rf_mem_if.v
Normal 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
|
||||
@@ -1,54 +0,0 @@
|
||||
`default_nettype none
|
||||
`include "../util/clog2.vh"
|
||||
|
||||
module serving_ram_dp
|
||||
#(// Memory parameters
|
||||
parameter depth = 256,
|
||||
parameter aw = `CLOG2(depth),
|
||||
parameter memfile = "",
|
||||
parameter sim = 1'b0)
|
||||
(
|
||||
// CPU port (compatible with serving_ram)
|
||||
input wire i_clk,
|
||||
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,
|
||||
|
||||
// Debug/programming port
|
||||
input wire i_dbg_clk,
|
||||
input wire [aw-1:0] i_dbg_addr,
|
||||
input wire [7:0] i_dbg_wdata,
|
||||
input wire i_dbg_wen,
|
||||
output wire [7:0] o_dbg_rdata
|
||||
);
|
||||
|
||||
reg [7:0] mem [0:depth-1] /* verilator public */;
|
||||
|
||||
always @(posedge i_clk) begin
|
||||
if (i_wen)
|
||||
mem[i_waddr] <= i_wdata;
|
||||
o_rdata <= mem[i_raddr];
|
||||
end
|
||||
|
||||
always @(posedge i_dbg_clk) begin
|
||||
if (i_dbg_wen)
|
||||
mem[i_dbg_addr] <= i_dbg_wdata;
|
||||
end
|
||||
|
||||
// Asynchronous debug read simplifies JTAG readback logic.
|
||||
assign o_dbg_rdata = mem[i_dbg_addr];
|
||||
|
||||
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
|
||||
@@ -1,65 +0,0 @@
|
||||
`default_nettype none
|
||||
`include "../util/clog2.vh"
|
||||
|
||||
// Drop-in serving RAM variant with USER JTAG programming access.
|
||||
module serving_ram_jtag
|
||||
#(
|
||||
parameter depth = 256,
|
||||
parameter aw = `CLOG2(depth),
|
||||
parameter memfile = "",
|
||||
parameter sim = 1'b0,
|
||||
parameter USER_CHAIN = 1
|
||||
)
|
||||
(
|
||||
input wire i_clk,
|
||||
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 wire [7:0] o_rdata
|
||||
);
|
||||
|
||||
wire dbg_clk;
|
||||
wire [aw-1:0] dbg_addr;
|
||||
wire [7:0] dbg_wdata;
|
||||
wire dbg_wen;
|
||||
wire [7:0] dbg_rdata;
|
||||
|
||||
serving_ram_dp
|
||||
#(
|
||||
.depth(depth),
|
||||
.aw(aw),
|
||||
.memfile(memfile),
|
||||
.sim(sim)
|
||||
)
|
||||
i_serving_ram_dp
|
||||
(
|
||||
.i_clk(i_clk),
|
||||
.i_waddr(i_waddr),
|
||||
.i_wdata(i_wdata),
|
||||
.i_wen(i_wen),
|
||||
.i_raddr(i_raddr),
|
||||
.o_rdata(o_rdata),
|
||||
.i_dbg_clk(dbg_clk),
|
||||
.i_dbg_addr(dbg_addr),
|
||||
.i_dbg_wdata(dbg_wdata),
|
||||
.i_dbg_wen(dbg_wen),
|
||||
.o_dbg_rdata(dbg_rdata)
|
||||
);
|
||||
|
||||
serving_ram_jtag_bridge
|
||||
#(
|
||||
.depth(depth),
|
||||
.aw(aw),
|
||||
.USER_CHAIN(USER_CHAIN)
|
||||
)
|
||||
i_serving_ram_jtag_bridge
|
||||
(
|
||||
.o_ram_clk(dbg_clk),
|
||||
.o_ram_addr(dbg_addr),
|
||||
.o_ram_wdata(dbg_wdata),
|
||||
.o_ram_wen(dbg_wen),
|
||||
.i_ram_rdata(dbg_rdata)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@@ -1,107 +0,0 @@
|
||||
`default_nettype none
|
||||
`include "../util/clog2.vh"
|
||||
|
||||
// Simple USER JTAG data-register protocol (LSB-first):
|
||||
// bit[0] : write_enable (1=write, 0=read/select)
|
||||
// bit[32:1] : 32-bit address
|
||||
// bit[40:33] : write data
|
||||
//
|
||||
// On UPDATE:
|
||||
// - write command: writes byte to RAM
|
||||
// - read command : updates current read address for next CAPTURE/SHIFT readback
|
||||
// - RAM uses the lower aw address bits from the 32-bit protocol address
|
||||
//
|
||||
// On CAPTURE, readback register loads:
|
||||
// bit[0] : valid (always 1)
|
||||
// bit[8:1] : read data at current read address
|
||||
// remaining bits : zero
|
||||
module serving_ram_jtag_bridge
|
||||
#(
|
||||
parameter depth = 256,
|
||||
parameter aw = `CLOG2(depth),
|
||||
parameter USER_CHAIN = 1
|
||||
)
|
||||
(
|
||||
output wire o_ram_clk,
|
||||
output wire [aw-1:0] o_ram_addr,
|
||||
output wire [7:0] o_ram_wdata,
|
||||
output wire o_ram_wen,
|
||||
input wire [7:0] i_ram_rdata
|
||||
);
|
||||
|
||||
localparam integer JTAG_AW = 32;
|
||||
localparam integer FRAME_W = 1 + JTAG_AW + 8;
|
||||
localparam integer PAD_W = FRAME_W - 9;
|
||||
|
||||
wire tap_drck;
|
||||
wire tap_shift;
|
||||
wire tap_update;
|
||||
wire tap_reset;
|
||||
wire tap_sel;
|
||||
wire tap_tdi;
|
||||
wire tap_tdo;
|
||||
|
||||
reg [FRAME_W-1:0] shift_in;
|
||||
reg [FRAME_W-1:0] shift_out;
|
||||
reg [aw-1:0] read_addr;
|
||||
reg shift_active_d;
|
||||
|
||||
wire cmd_write;
|
||||
wire [JTAG_AW-1:0] cmd_addr;
|
||||
wire [aw-1:0] cmd_addr_ram;
|
||||
wire [7:0] cmd_wdata;
|
||||
|
||||
assign cmd_write = shift_in[0];
|
||||
assign cmd_addr = shift_in[JTAG_AW:1];
|
||||
assign cmd_wdata = shift_in[JTAG_AW+8:JTAG_AW+1];
|
||||
assign cmd_addr_ram = cmd_addr[aw-1:0];
|
||||
|
||||
// Update command shift register and shift response out on DRCK.
|
||||
// Readback data is loaded on the first shift pulse of a DR scan.
|
||||
always @(posedge tap_drck or posedge tap_reset) begin
|
||||
if (tap_reset) begin
|
||||
shift_in <= {FRAME_W{1'b0}};
|
||||
shift_out <= {FRAME_W{1'b0}};
|
||||
shift_active_d <= 1'b0;
|
||||
end else if (tap_sel && tap_shift) begin
|
||||
if (!shift_active_d)
|
||||
shift_out <= {{PAD_W{1'b0}}, i_ram_rdata, 1'b1};
|
||||
else
|
||||
shift_out <= {1'b0, shift_out[FRAME_W-1:1]};
|
||||
shift_in <= {tap_tdi, shift_in[FRAME_W-1:1]};
|
||||
shift_active_d <= 1'b1;
|
||||
end else begin
|
||||
shift_active_d <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// Read command selects the address for the next capture.
|
||||
always @(posedge tap_update or posedge tap_reset) begin
|
||||
if (tap_reset)
|
||||
read_addr <= {aw{1'b0}};
|
||||
else if (tap_sel && !cmd_write)
|
||||
read_addr <= cmd_addr_ram;
|
||||
end
|
||||
|
||||
assign o_ram_clk = tap_update;
|
||||
assign o_ram_wen = tap_update & tap_sel & cmd_write;
|
||||
assign o_ram_wdata = cmd_wdata;
|
||||
assign o_ram_addr = tap_update ? cmd_addr_ram : read_addr;
|
||||
|
||||
assign tap_tdo = shift_out[0];
|
||||
|
||||
jtag_tap_spartan6
|
||||
#(.USER_CHAIN(USER_CHAIN))
|
||||
i_jtag_tap
|
||||
(
|
||||
.o_drck(tap_drck),
|
||||
.o_capture(),
|
||||
.o_shift(tap_shift),
|
||||
.o_update(tap_update),
|
||||
.o_reset(tap_reset),
|
||||
.o_sel(tap_sel),
|
||||
.o_tdi(tap_tdi),
|
||||
.i_tdo(tap_tdo)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@@ -1,17 +1,18 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module top_generic(
|
||||
module top_generic #(
|
||||
parameter sim = 0
|
||||
)(
|
||||
input wire aclk,
|
||||
input wire aresetn,
|
||||
|
||||
output wire led_green,
|
||||
output wire led_red,
|
||||
|
||||
output wire[5:0] r2r
|
||||
output wire[5:0] r2r,
|
||||
output wire[7:0] LED
|
||||
);
|
||||
`include "conv.vh"
|
||||
assign led_green = 1'b0;
|
||||
assign led_red = 1'b0;
|
||||
|
||||
// Clocking
|
||||
wire clk_100;
|
||||
@@ -27,8 +28,11 @@ module top_generic(
|
||||
wire [31:0] GPIO_C;
|
||||
wire [31:0] GPIO_D;
|
||||
|
||||
soclet #(
|
||||
.memfile("../sw/sweep/sweep.hex")
|
||||
wire test;
|
||||
|
||||
mcu #(
|
||||
.memfile("../sw/sweep/sweep.hex"),
|
||||
.sim(sim)
|
||||
) mcu (
|
||||
.i_clk(clk_15),
|
||||
.i_rst(!aresetn),
|
||||
@@ -39,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)
|
||||
);
|
||||
|
||||
|
||||
@@ -62,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
|
||||
|
||||
75
rtl/toplevel/top_jtag.v
Normal file
75
rtl/toplevel/top_jtag.v
Normal file
@@ -0,0 +1,75 @@
|
||||
module top_jtag(
|
||||
input wire aclk,
|
||||
input wire aresetn,
|
||||
|
||||
output wire led_green,
|
||||
output wire led_red,
|
||||
output wire [7:0] LED,
|
||||
|
||||
output wire [5:0] r2r
|
||||
);
|
||||
// Clocking
|
||||
wire clk_100;
|
||||
wire clk_15;
|
||||
assign clk_100 = aclk;
|
||||
clk_gen clk(
|
||||
.clk_in(clk_100),
|
||||
.clk_out_15(clk_15)
|
||||
);
|
||||
|
||||
wire i_rst;
|
||||
assign i_rst = !aresetn;
|
||||
|
||||
wire [31:0] wb_adr;
|
||||
wire [31:0] wb_dat;
|
||||
wire [31:0] wb_rdt;
|
||||
wire [3:0] wb_sel;
|
||||
wire wb_cyc;
|
||||
wire wb_we;
|
||||
wire wb_stb;
|
||||
wire wb_ack;
|
||||
wire cmd_reset;
|
||||
|
||||
jtag_wb_bridge #(
|
||||
.chain(1)
|
||||
) jtag_wb (
|
||||
.i_clk(clk_15),
|
||||
.i_rst(!aresetn),
|
||||
|
||||
.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)
|
||||
);
|
||||
|
||||
wire [31:0] gpio;
|
||||
wire [31:0] gpio_in;
|
||||
assign gpio_in = 32'h0;
|
||||
|
||||
wb_gpio #(
|
||||
.address(32'h00000000)
|
||||
) u_wb_gpio (
|
||||
.i_wb_clk(clk_15),
|
||||
.i_wb_rst(i_rst | cmd_reset),
|
||||
.i_wb_adr(wb_adr),
|
||||
.i_wb_dat(wb_dat),
|
||||
.i_wb_sel(wb_sel),
|
||||
.i_wb_we(wb_we),
|
||||
.i_wb_stb(wb_stb & wb_cyc),
|
||||
.i_gpio(gpio_in),
|
||||
.o_wb_rdt(wb_rdt),
|
||||
.o_wb_ack(wb_ack),
|
||||
.o_gpio(gpio)
|
||||
);
|
||||
|
||||
assign LED = gpio[7:0];
|
||||
assign r2r = gpio[13:8];
|
||||
assign led_green = gpio[30];
|
||||
assign led_red = gpio[31];
|
||||
|
||||
endmodule
|
||||
196
rtl/wb/jtag_wb_bridge.v
Normal file
196
rtl/wb/jtag_wb_bridge.v
Normal file
@@ -0,0 +1,196 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module jtag_wb_bridge #(
|
||||
parameter integer chain = 1,
|
||||
// 0: Use cmd_addr[1:0] to select byte lane on 32-bit WB data bus.
|
||||
// 1: Always use lane 0 (LSB), for byte-wide memories that return data in [7:0].
|
||||
parameter integer byte_aligned = 0
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
|
||||
output wire [31:0] o_wb_adr,
|
||||
output wire [31:0] o_wb_dat,
|
||||
output wire [3:0] o_wb_sel,
|
||||
output wire o_wb_we,
|
||||
output wire o_wb_cyc,
|
||||
output wire o_wb_stb,
|
||||
input wire [31:0] i_wb_rdt,
|
||||
input wire i_wb_ack,
|
||||
|
||||
output wire o_cmd_reset
|
||||
);
|
||||
// JTAG interface wires
|
||||
wire jtag_tck;
|
||||
wire jtag_tdi;
|
||||
wire jtag_drck;
|
||||
wire jtag_capture;
|
||||
wire jtag_shift;
|
||||
wire jtag_update;
|
||||
wire jtag_runtest;
|
||||
wire jtag_reset;
|
||||
wire jtag_sel;
|
||||
|
||||
reg [41:0] jtag_q;
|
||||
wire [41:0] jtag_data_in;
|
||||
wire jtag_async_reset;
|
||||
|
||||
jtag_if #(
|
||||
.chain(chain)
|
||||
) jtag (
|
||||
.i_tdo(jtag_q[0]),
|
||||
.o_tck(jtag_tck),
|
||||
.o_tdi(jtag_tdi),
|
||||
.o_drck(jtag_drck),
|
||||
.o_capture(jtag_capture),
|
||||
.o_shift(jtag_shift),
|
||||
.o_update(jtag_update),
|
||||
.o_runtest(jtag_runtest),
|
||||
.o_reset(jtag_reset),
|
||||
.o_sel(jtag_sel)
|
||||
);
|
||||
|
||||
assign jtag_async_reset = jtag_reset || i_rst;
|
||||
|
||||
// JTAG shift register behavior
|
||||
always @(posedge jtag_drck or posedge jtag_async_reset) begin
|
||||
if (jtag_async_reset) begin
|
||||
jtag_q <= 42'b0;
|
||||
end else if (jtag_sel && jtag_capture) begin
|
||||
jtag_q <= jtag_data_in;
|
||||
end else if (jtag_sel && jtag_shift) begin
|
||||
jtag_q <= {jtag_tdi, jtag_q[41:1]};
|
||||
end
|
||||
end
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// JTAG -> i_clk crossing using toggle request/ack handshake.
|
||||
// Command packet format: [41]=we, [40]=reset, [39:8]=addr, [7:0]=wdata
|
||||
// -----------------------------------------------------------------------------
|
||||
reg [41:0] j_cmd_hold;
|
||||
reg j_req_tgl;
|
||||
reg j_ack_sync_1;
|
||||
reg j_ack_sync_2;
|
||||
|
||||
reg s_ack_tgl;
|
||||
reg s_req_sync_1;
|
||||
reg s_req_sync_2;
|
||||
reg s_req_sync_3;
|
||||
reg [41:0] s_cmd_sync_1;
|
||||
reg [41:0] s_cmd_sync_2;
|
||||
|
||||
always @(posedge jtag_drck or posedge jtag_async_reset) begin
|
||||
if (jtag_async_reset) begin
|
||||
j_ack_sync_1 <= 1'b0;
|
||||
j_ack_sync_2 <= 1'b0;
|
||||
end else begin
|
||||
j_ack_sync_1 <= s_ack_tgl;
|
||||
j_ack_sync_2 <= j_ack_sync_1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge jtag_update or posedge jtag_async_reset) begin
|
||||
if (jtag_async_reset) begin
|
||||
j_cmd_hold <= 42'b0;
|
||||
j_req_tgl <= 1'b0;
|
||||
end else if (jtag_sel && (j_ack_sync_2 == j_req_tgl)) begin
|
||||
j_cmd_hold <= jtag_q;
|
||||
j_req_tgl <= ~j_req_tgl;
|
||||
end
|
||||
end
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Wishbone classic single-request master (1 outstanding transaction max).
|
||||
// -----------------------------------------------------------------------------
|
||||
reg wb_busy;
|
||||
reg [31:0] wb_adr_r;
|
||||
reg [31:0] wb_dat_r;
|
||||
reg [3:0] wb_sel_r;
|
||||
reg wb_we_r;
|
||||
reg cmd_reset_pulse_r;
|
||||
reg [31:0] resp_addr_r;
|
||||
reg [7:0] resp_data_r;
|
||||
|
||||
wire req_pulse;
|
||||
wire [7:0] cmd_wdata;
|
||||
wire [31:0] cmd_addr;
|
||||
wire cmd_reset;
|
||||
wire cmd_we;
|
||||
wire [1:0] req_lane;
|
||||
wire [1:0] resp_lane;
|
||||
|
||||
assign req_pulse = s_req_sync_2 ^ s_req_sync_3;
|
||||
assign cmd_wdata = s_cmd_sync_2[7:0];
|
||||
assign cmd_addr = s_cmd_sync_2[39:8];
|
||||
assign cmd_reset = s_cmd_sync_2[40];
|
||||
assign cmd_we = s_cmd_sync_2[41];
|
||||
assign req_lane = byte_aligned ? 2'b00 : cmd_addr[1:0];
|
||||
assign resp_lane = byte_aligned ? 2'b00 : wb_adr_r[1:0];
|
||||
|
||||
assign o_wb_adr = wb_adr_r;
|
||||
assign o_wb_dat = wb_dat_r;
|
||||
assign o_wb_sel = wb_sel_r;
|
||||
assign o_wb_we = wb_we_r;
|
||||
assign o_wb_cyc = wb_busy;
|
||||
assign o_wb_stb = wb_busy;
|
||||
assign o_cmd_reset = cmd_reset_pulse_r;
|
||||
|
||||
always @(posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
s_ack_tgl <= 1'b0;
|
||||
s_req_sync_1 <= 1'b0;
|
||||
s_req_sync_2 <= 1'b0;
|
||||
s_req_sync_3 <= 1'b0;
|
||||
s_cmd_sync_1 <= 42'b0;
|
||||
s_cmd_sync_2 <= 42'b0;
|
||||
wb_busy <= 1'b0;
|
||||
wb_adr_r <= 32'b0;
|
||||
wb_dat_r <= 32'b0;
|
||||
wb_sel_r <= 4'b0000;
|
||||
wb_we_r <= 1'b0;
|
||||
cmd_reset_pulse_r <= 1'b0;
|
||||
resp_addr_r <= 32'b0;
|
||||
resp_data_r <= 8'b0;
|
||||
end else begin
|
||||
s_req_sync_1 <= j_req_tgl;
|
||||
s_req_sync_2 <= s_req_sync_1;
|
||||
s_req_sync_3 <= s_req_sync_2;
|
||||
s_cmd_sync_1 <= j_cmd_hold;
|
||||
s_cmd_sync_2 <= s_cmd_sync_1;
|
||||
cmd_reset_pulse_r <= 1'b0;
|
||||
|
||||
if (req_pulse && !wb_busy) begin
|
||||
wb_busy <= 1'b1;
|
||||
wb_we_r <= cmd_we;
|
||||
wb_adr_r <= cmd_addr;
|
||||
|
||||
case (req_lane)
|
||||
2'b00: begin wb_sel_r <= 4'b0001; wb_dat_r <= {24'b0, cmd_wdata}; end
|
||||
2'b01: begin wb_sel_r <= 4'b0010; wb_dat_r <= {16'b0, cmd_wdata, 8'b0}; end
|
||||
2'b10: begin wb_sel_r <= 4'b0100; wb_dat_r <= {8'b0, cmd_wdata, 16'b0}; end
|
||||
default: begin wb_sel_r <= 4'b1000; wb_dat_r <= {cmd_wdata, 24'b0}; end
|
||||
endcase
|
||||
|
||||
cmd_reset_pulse_r <= cmd_reset;
|
||||
end
|
||||
|
||||
if (wb_busy && i_wb_ack) begin
|
||||
wb_busy <= 1'b0;
|
||||
wb_we_r <= 1'b0;
|
||||
resp_addr_r <= wb_adr_r;
|
||||
|
||||
case (resp_lane)
|
||||
2'b00: resp_data_r <= i_wb_rdt[7:0];
|
||||
2'b01: resp_data_r <= i_wb_rdt[15:8];
|
||||
2'b10: resp_data_r <= i_wb_rdt[23:16];
|
||||
default: resp_data_r <= i_wb_rdt[31:24];
|
||||
endcase
|
||||
|
||||
s_ack_tgl <= s_req_sync_2;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assign jtag_data_in = {2'b00, resp_addr_r, resp_data_r};
|
||||
|
||||
endmodule
|
||||
145
rtl/wb/wb_mux.v
Normal file
145
rtl/wb/wb_mux.v
Normal 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
74
rtl/wb/wb_timer.v
Normal 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
|
||||
@@ -1,196 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Write a file into serving_ram_jtag over Spartan-6 USER JTAG via OpenOCD.
|
||||
|
||||
This script targets the protocol implemented by rtl/serv/serving_ram_jtag_bridge.v:
|
||||
frame bit[0] = write_enable
|
||||
frame bit[32:1] = 32-bit address
|
||||
frame bit[40:33] = data byte
|
||||
|
||||
Notes:
|
||||
- Frame is shifted LSB-first (OpenOCD drscan integer value format matches this).
|
||||
- USER1/USER2 opcode selection is Spartan-6 specific (IR opcodes 0x02/0x03, IR length 6).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
JTAG_ADDR_W = 32
|
||||
JTAG_FRAME_W = 1 + JTAG_ADDR_W + 8
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
p = argparse.ArgumentParser(description="Write file to serving RAM over JTAG")
|
||||
p.add_argument("input", help="Input file (.bin or readmemh-style .hex/.mem)")
|
||||
p.add_argument(
|
||||
"--ram-addr-width",
|
||||
"--addr-width",
|
||||
dest="ram_addr_width",
|
||||
type=int,
|
||||
default=8,
|
||||
help="RAM address width (aw) in HDL, default: 8",
|
||||
)
|
||||
p.add_argument("--base-addr", type=lambda x: int(x, 0), default=0, help="Base address for .bin input")
|
||||
p.add_argument("--tap", default="xc6s.tap", help="OpenOCD tap name (default: xc6s.tap)")
|
||||
p.add_argument(
|
||||
"--user-chain",
|
||||
type=int,
|
||||
choices=[1, 2],
|
||||
default=1,
|
||||
help="BSCAN user chain used in HDL (default: 1)",
|
||||
)
|
||||
p.add_argument("--openocd-cfg", action="append", default=[], help="OpenOCD -f config file (repeatable)")
|
||||
p.add_argument("--openocd-cmd", action="append", default=[], help="Extra OpenOCD -c command before programming")
|
||||
p.add_argument("--limit", type=int, default=None, help="Write only first N bytes")
|
||||
p.add_argument("--dry-run", action="store_true", help="Generate and print TCL only")
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
def _strip_line_comments(line: str) -> str:
|
||||
return line.split("//", 1)[0]
|
||||
|
||||
|
||||
def parse_readmemh_text(path: pathlib.Path) -> Dict[int, int]:
|
||||
"""Parse a simple readmemh-style file with optional @address directives."""
|
||||
text = path.read_text(encoding="utf-8")
|
||||
words: Dict[int, int] = {}
|
||||
addr = 0
|
||||
|
||||
for raw_line in text.splitlines():
|
||||
line = _strip_line_comments(raw_line).strip()
|
||||
if not line:
|
||||
continue
|
||||
for tok in line.split():
|
||||
tok = tok.strip()
|
||||
if not tok:
|
||||
continue
|
||||
if tok.startswith("@"):
|
||||
addr = int(tok[1:], 16)
|
||||
continue
|
||||
if not re.fullmatch(r"[0-9a-fA-F]+", tok):
|
||||
raise ValueError(f"Unsupported token '{tok}' in {path}")
|
||||
val = int(tok, 16)
|
||||
if val < 0 or val > 0xFF:
|
||||
raise ValueError(f"Byte value out of range at address 0x{addr:x}: {tok}")
|
||||
words[addr] = val
|
||||
addr += 1
|
||||
|
||||
return words
|
||||
|
||||
|
||||
def load_image(path: pathlib.Path, base_addr: int) -> List[Tuple[int, int]]:
|
||||
suffix = path.suffix.lower()
|
||||
if suffix == ".bin":
|
||||
blob = path.read_bytes()
|
||||
return [(base_addr + i, b) for i, b in enumerate(blob)]
|
||||
if suffix in {".hex", ".mem", ".vmem"}:
|
||||
words = parse_readmemh_text(path)
|
||||
return sorted(words.items())
|
||||
raise ValueError("Unsupported input format. Use .bin, .hex, .mem, or .vmem")
|
||||
|
||||
|
||||
def build_write_frame(addr: int, data: int) -> int:
|
||||
return (data << (JTAG_ADDR_W + 1)) | ((addr & ((1 << JTAG_ADDR_W) - 1)) << 1) | 0x1
|
||||
|
||||
|
||||
def build_openocd_tcl(entries: List[Tuple[int, int]], tap: str, user_chain: int, pre_cmds: List[str]) -> str:
|
||||
ir_opcode = 0x02 if user_chain == 1 else 0x03
|
||||
|
||||
lines: List[str] = []
|
||||
lines.append("init")
|
||||
for cmd in pre_cmds:
|
||||
lines.append(cmd)
|
||||
lines.append(f"irscan {tap} 0x{ir_opcode:x} -endstate IDLE")
|
||||
|
||||
for addr, data in entries:
|
||||
frame = build_write_frame(addr, data)
|
||||
lines.append(f"drscan {tap} {JTAG_FRAME_W} 0x{frame:x} -endstate IDLE")
|
||||
|
||||
lines.append("shutdown")
|
||||
lines.append("")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def run_openocd(cfg_files: List[str], script_path: pathlib.Path) -> int:
|
||||
cmd = ["openocd"]
|
||||
for cfg in cfg_files:
|
||||
cmd += ["-f", cfg]
|
||||
cmd += ["-f", str(script_path)]
|
||||
|
||||
proc = subprocess.run(cmd)
|
||||
return proc.returncode
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
in_path = pathlib.Path(args.input)
|
||||
|
||||
if not in_path.exists():
|
||||
print(f"error: input file not found: {in_path}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
entries = load_image(in_path, args.base_addr)
|
||||
if args.limit is not None:
|
||||
entries = entries[: args.limit]
|
||||
|
||||
if not entries:
|
||||
print("error: no bytes found to write", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
if args.ram_addr_width < 1 or args.ram_addr_width > JTAG_ADDR_W:
|
||||
print(
|
||||
f"error: --ram-addr-width must be in [1, {JTAG_ADDR_W}] for this protocol",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
max_jtag_addr = (1 << JTAG_ADDR_W) - 1
|
||||
max_addr = (1 << args.ram_addr_width) - 1
|
||||
for addr, _ in entries:
|
||||
if addr < 0 or addr > max_jtag_addr:
|
||||
print(
|
||||
f"error: address 0x{addr:x} exceeds 32-bit protocol range (max 0x{max_jtag_addr:x})",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
if addr > max_addr:
|
||||
print(
|
||||
f"error: address 0x{addr:x} exceeds RAM addr width {args.ram_addr_width} (max 0x{max_addr:x})",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
tcl = build_openocd_tcl(entries, args.tap, args.user_chain, args.openocd_cmd)
|
||||
|
||||
if args.dry_run:
|
||||
print(tcl, end="")
|
||||
print(f"# bytes: {len(entries)}", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
if not args.openocd_cfg:
|
||||
print("error: provide at least one --openocd-cfg unless using --dry-run", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".tcl", delete=False) as tf:
|
||||
tf.write(tcl)
|
||||
tcl_path = pathlib.Path(tf.name)
|
||||
|
||||
print(f"Programming {len(entries)} bytes via JTAG...")
|
||||
rc = run_openocd(args.openocd_cfg, tcl_path)
|
||||
if rc != 0:
|
||||
print(f"error: openocd failed with exit code {rc}", file=sys.stderr)
|
||||
print(f"TCL kept at: {tcl_path}", file=sys.stderr)
|
||||
return rc
|
||||
|
||||
print("Done.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
10
sim/other/test.svf
Normal file
10
sim/other/test.svf
Normal file
@@ -0,0 +1,10 @@
|
||||
TRST ABSENT;
|
||||
ENDIR IDLE;
|
||||
ENDDR IDLE;
|
||||
STATE RESET;
|
||||
STATE IDLE;
|
||||
SIR 6 TDI (02);
|
||||
|
||||
SDR 42 TDI (3A987654321);
|
||||
|
||||
STATE IDLE;
|
||||
196
sim/overrides/jtag_if.v
Normal file
196
sim/overrides/jtag_if.v
Normal file
@@ -0,0 +1,196 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
// =============================================================================
|
||||
// JTAG interface (simulation override)
|
||||
// Behavioral SVF player for simple USER-chain simulation.
|
||||
//
|
||||
// Supported SVF commands (line-oriented, uppercase recommended):
|
||||
// - SIR <n> TDI (<hex>);
|
||||
// - SDR <n> TDI (<hex>);
|
||||
// - RUNTEST <n> TCK;
|
||||
// - STATE RESET;
|
||||
// - STATE IDLE;
|
||||
// Other lines are ignored.
|
||||
// =============================================================================
|
||||
module jtag_if #(
|
||||
parameter integer chain = 1,
|
||||
parameter [8*256-1:0] SVF_FILE = "write_jtag.svf",
|
||||
parameter integer TCK_HALF_PERIOD_NS = 50,
|
||||
parameter [31:0] USER_IR_OPCODE = 32'h00000002
|
||||
)(
|
||||
input wire i_tdo,
|
||||
output reg o_tck,
|
||||
output reg o_tdi,
|
||||
output reg o_drck,
|
||||
output reg o_capture,
|
||||
output reg o_shift,
|
||||
output reg o_update,
|
||||
output reg o_runtest,
|
||||
output reg o_reset,
|
||||
output reg o_sel
|
||||
);
|
||||
integer fd;
|
||||
integer got;
|
||||
integer nbits;
|
||||
integer cycles;
|
||||
integer i;
|
||||
integer line_no;
|
||||
integer scan_bits;
|
||||
|
||||
reg [8*1024-1:0] line;
|
||||
reg [8*32-1:0] cmd;
|
||||
reg [8*32-1:0] arg1;
|
||||
reg [8*256-1:0] svf_file_path;
|
||||
reg [4095:0] scan_data;
|
||||
reg [31:0] current_ir;
|
||||
reg selected;
|
||||
|
||||
// Keep linters quiet: TDO is not consumed by this simple player.
|
||||
wire _unused_tdo;
|
||||
assign _unused_tdo = i_tdo;
|
||||
|
||||
task pulse_tck;
|
||||
begin
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_drck_shift;
|
||||
input bit_in;
|
||||
begin
|
||||
o_tdi = bit_in;
|
||||
o_shift = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b1;
|
||||
o_drck = 1'b1;
|
||||
end
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b0;
|
||||
o_drck = 1'b0;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_capture;
|
||||
begin
|
||||
o_capture = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b1;
|
||||
o_drck = 1'b1;
|
||||
end
|
||||
#TCK_HALF_PERIOD_NS begin
|
||||
o_tck = 1'b0;
|
||||
o_drck = 1'b0;
|
||||
o_capture = 1'b0;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task pulse_update;
|
||||
begin
|
||||
o_shift = 1'b0;
|
||||
#TCK_HALF_PERIOD_NS o_update = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_update = 1'b0;
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
// Default output levels
|
||||
o_tck = 1'b0;
|
||||
o_tdi = 1'b0;
|
||||
o_drck = 1'b0;
|
||||
o_capture = 1'b0;
|
||||
o_shift = 1'b0;
|
||||
o_update = 1'b0;
|
||||
o_runtest = 1'b0;
|
||||
o_reset = 1'b0;
|
||||
o_sel = 1'b0;
|
||||
selected = 1'b0;
|
||||
current_ir = 32'h0;
|
||||
line_no = 0;
|
||||
svf_file_path = SVF_FILE;
|
||||
|
||||
// Deterministic startup reset pulse before consuming SVF.
|
||||
o_reset = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b0;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_tck = 1'b0;
|
||||
o_reset = 1'b0;
|
||||
|
||||
fd = $fopen(svf_file_path, "r");
|
||||
if (fd == 0) begin
|
||||
$display("jtag_if(sim): could not open SVF file '%0s'", svf_file_path);
|
||||
end else begin
|
||||
while (!$feof(fd)) begin
|
||||
line = {8*1024{1'b0}};
|
||||
got = $fgets(line, fd);
|
||||
line_no = line_no + 1;
|
||||
if (got > 0) begin
|
||||
cmd = {8*32{1'b0}};
|
||||
arg1 = {8*32{1'b0}};
|
||||
scan_data = {4096{1'b0}};
|
||||
nbits = 0;
|
||||
cycles = 0;
|
||||
|
||||
got = $sscanf(line, "%s", cmd);
|
||||
if (got < 1) begin
|
||||
// Empty line
|
||||
end else if (cmd == "!") begin
|
||||
// Comment line
|
||||
end else if (cmd == "SIR") begin
|
||||
got = $sscanf(line, "SIR %d TDI (%h);", nbits, scan_data);
|
||||
if (got == 2) begin
|
||||
current_ir = scan_data[31:0];
|
||||
selected = (current_ir == USER_IR_OPCODE);
|
||||
o_sel = selected;
|
||||
|
||||
// Emit TCK pulses for IR shift activity.
|
||||
scan_bits = nbits;
|
||||
if (scan_bits > 4096) scan_bits = 4096;
|
||||
for (i = 0; i < scan_bits; i = i + 1) begin
|
||||
o_tdi = scan_data[i];
|
||||
pulse_tck;
|
||||
end
|
||||
end
|
||||
end else if (cmd == "SDR") begin
|
||||
got = $sscanf(line, "SDR %d TDI (%h);", nbits, scan_data);
|
||||
if (got == 2) begin
|
||||
if (selected) begin
|
||||
pulse_capture;
|
||||
end
|
||||
scan_bits = nbits;
|
||||
if (scan_bits > 4096) scan_bits = 4096;
|
||||
for (i = 0; i < scan_bits; i = i + 1) begin
|
||||
pulse_drck_shift(scan_data[i]);
|
||||
end
|
||||
if (selected) begin
|
||||
pulse_update;
|
||||
end
|
||||
end
|
||||
end else if (cmd == "RUNTEST") begin
|
||||
got = $sscanf(line, "RUNTEST %d TCK;", cycles);
|
||||
if (got == 1) begin
|
||||
o_runtest = 1'b1;
|
||||
for (i = 0; i < cycles; i = i + 1)
|
||||
pulse_tck;
|
||||
o_runtest = 1'b0;
|
||||
end
|
||||
end else if (cmd == "STATE") begin
|
||||
got = $sscanf(line, "STATE %s", arg1);
|
||||
if (got == 1) begin
|
||||
if (arg1 == "RESET;") begin
|
||||
o_reset = 1'b1;
|
||||
#TCK_HALF_PERIOD_NS o_reset = 1'b0;
|
||||
selected = 1'b0;
|
||||
o_sel = 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
$fclose(fd);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
106
sim/tb/tb_svf.v
Normal file
106
sim/tb/tb_svf.v
Normal file
@@ -0,0 +1,106 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_svf();
|
||||
reg clk;
|
||||
reg resetn;
|
||||
initial clk <= 1'b0;
|
||||
initial resetn <= 1'b0;
|
||||
always #33.33 clk <= !clk;
|
||||
initial #40 resetn <= 1'b1;
|
||||
|
||||
wire [7:0] led_out;
|
||||
|
||||
jtag_byte_sink #(
|
||||
.SVF_FILE("sim/other/test.svf"),
|
||||
.TCK_HALF_PERIOD_NS(500)
|
||||
) dut (
|
||||
.i_clk(clk),
|
||||
.i_rst(!resetn),
|
||||
.o_led(led_out)
|
||||
);
|
||||
|
||||
initial begin
|
||||
$dumpfile("out.vcd");
|
||||
$dumpvars;
|
||||
#200_000;
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module jtag_byte_sink #(
|
||||
parameter [8*256-1:0] SVF_FILE = "",
|
||||
parameter integer TCK_HALF_PERIOD_NS = 50
|
||||
)(
|
||||
input wire i_clk,
|
||||
input wire i_rst,
|
||||
output reg [7:0] o_led
|
||||
);
|
||||
|
||||
initial o_led <= 0;
|
||||
|
||||
// JTAG interface wires
|
||||
wire jtag_tck;
|
||||
wire jtag_tdi;
|
||||
wire jtag_drck;
|
||||
wire jtag_capture;
|
||||
wire jtag_shift;
|
||||
wire jtag_update;
|
||||
wire jtag_runtest;
|
||||
wire jtag_reset;
|
||||
wire jtag_sel;
|
||||
|
||||
reg [41:0] jtag_q;
|
||||
reg [41:0] jtag_data;
|
||||
wire jtag_async_reset;
|
||||
|
||||
jtag_if #(
|
||||
.chain(1),
|
||||
.SVF_FILE(SVF_FILE),
|
||||
.TCK_HALF_PERIOD_NS(TCK_HALF_PERIOD_NS),
|
||||
.USER_IR_OPCODE(32'h0000_0002)
|
||||
) jtag (
|
||||
.i_tdo(jtag_q[0]),
|
||||
.o_tck(jtag_tck),
|
||||
.o_tdi(jtag_tdi),
|
||||
.o_drck(jtag_drck),
|
||||
.o_capture(jtag_capture),
|
||||
.o_shift(jtag_shift),
|
||||
.o_update(jtag_update),
|
||||
.o_runtest(jtag_runtest),
|
||||
.o_reset(jtag_reset),
|
||||
.o_sel(jtag_sel)
|
||||
);
|
||||
|
||||
|
||||
assign jtag_async_reset = jtag_reset || i_rst;
|
||||
|
||||
always @(posedge jtag_drck or posedge jtag_async_reset) begin
|
||||
if (jtag_async_reset) begin
|
||||
jtag_q <= 0;
|
||||
end else if (jtag_sel && jtag_capture) begin
|
||||
jtag_q <= jtag_data;
|
||||
end else if (jtag_sel && jtag_shift) begin
|
||||
jtag_q <= {jtag_tdi, jtag_q[41:1]};
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge jtag_update or posedge jtag_async_reset) begin
|
||||
if (jtag_async_reset) begin
|
||||
jtag_data <= 0;
|
||||
end else if (jtag_sel) begin
|
||||
jtag_data <= jtag_q;
|
||||
end
|
||||
end
|
||||
|
||||
wire [41:0] j_data;
|
||||
wire j_data_update;
|
||||
cdc_strobed #(42) j_data_cdc (
|
||||
.i_clk_a(i_clk),
|
||||
.i_clk_b(i_clk),
|
||||
.i_data(jtag_data),
|
||||
.i_strobe(jtag_update),
|
||||
.o_data(j_data),
|
||||
.o_strobe(j_data_update)
|
||||
);
|
||||
|
||||
endmodule
|
||||
45
sim/tb/tb_top_generic.v
Normal file
45
sim/tb/tb_top_generic.v
Normal 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
157
sim/tb/tb_wb_timer.v
Normal 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
|
||||
@@ -3,7 +3,7 @@ ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
||||
@@ -3,7 +3,7 @@ ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
tools/.gitignore
vendored
Normal file
2
tools/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
test
|
||||
32
tools/Makefile
Normal file
32
tools/Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
TOOLCHAIN_PREFIX ?=
|
||||
|
||||
CC := $(TOOLCHAIN_PREFIX)g++
|
||||
|
||||
TARGET := test
|
||||
SRCS_C :=
|
||||
SRCS_CPP:= test.cpp digilent_jtag.cpp argparse.cpp
|
||||
OBJS := $(SRCS_C:.c=.o) $(SRCS_CPP:.cpp=.o)
|
||||
|
||||
ADEPT_LIBDIR := /opt/packages/digilent.adept.runtime_2.27.9-x86_64/lib64
|
||||
|
||||
CFLAGS :=
|
||||
ASFLAGS :=
|
||||
LDFLAGS := -L$(ADEPT_LIBDIR) -Wl,--disable-new-dtags -Wl,-rpath,$(ADEPT_LIBDIR)
|
||||
LIBS := -ldjtg -ldmgr -ldpcomm -ldabs -ldftd2xx
|
||||
|
||||
|
||||
.PHONY: all clean size
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(OBJS)
|
||||
158
tools/argparse.cpp
Normal file
158
tools/argparse.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "argparse.hpp"
|
||||
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
ArgParser::ArgParser(std::string program_name)
|
||||
: program_name_(std::move(program_name)) {}
|
||||
|
||||
void ArgParser::addString(const std::string &name,
|
||||
const std::string &default_value,
|
||||
const std::string &help,
|
||||
bool required) {
|
||||
order_.push_back(name);
|
||||
meta_[name] = {OptionType::kString, help, required};
|
||||
string_values_[name] = default_value;
|
||||
provided_[name] = false;
|
||||
}
|
||||
|
||||
void ArgParser::addInt(const std::string &name,
|
||||
int default_value,
|
||||
const std::string &help,
|
||||
bool required) {
|
||||
order_.push_back(name);
|
||||
meta_[name] = {OptionType::kInt, help, required};
|
||||
int_values_[name] = default_value;
|
||||
provided_[name] = false;
|
||||
}
|
||||
|
||||
bool ArgParser::parse(int argc, char **argv, std::string *error) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string token(argv[i]);
|
||||
if (token == "--help" || token == "-h") {
|
||||
if (error) {
|
||||
*error = "help";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (token.rfind("--", 0) != 0) {
|
||||
if (error) {
|
||||
*error = "Unexpected positional argument: " + token;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
size_t eq = token.find('=');
|
||||
if (eq == std::string::npos) {
|
||||
key = token.substr(2);
|
||||
if (i + 1 >= argc) {
|
||||
if (error) {
|
||||
*error = "Missing value for --" + key;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
value = argv[++i];
|
||||
} else {
|
||||
key = token.substr(2, eq - 2);
|
||||
value = token.substr(eq + 1);
|
||||
}
|
||||
|
||||
auto m = meta_.find(key);
|
||||
if (m == meta_.end()) {
|
||||
if (error) {
|
||||
*error = "Unknown option: --" + key;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m->second.type == OptionType::kString) {
|
||||
string_values_[key] = value;
|
||||
provided_[key] = true;
|
||||
} else {
|
||||
errno = 0;
|
||||
char *endp = nullptr;
|
||||
long parsed = std::strtol(value.c_str(), &endp, 0);
|
||||
if (errno != 0 || endp == value.c_str() || *endp != '\0' ||
|
||||
parsed < INT_MIN || parsed > INT_MAX) {
|
||||
if (error) {
|
||||
*error = "Invalid integer for --" + key + ": " + value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int_values_[key] = static_cast<int>(parsed);
|
||||
provided_[key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &key : order_) {
|
||||
auto m = meta_.find(key);
|
||||
if (m != meta_.end() && m->second.required && !has(key)) {
|
||||
if (error) {
|
||||
*error = "Missing required option: --" + key;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArgParser::has(const std::string &name) const {
|
||||
auto p = provided_.find(name);
|
||||
return p != provided_.end() && p->second;
|
||||
}
|
||||
|
||||
std::string ArgParser::getString(const std::string &name) const {
|
||||
auto it = string_values_.find(name);
|
||||
if (it == string_values_.end()) {
|
||||
return std::string();
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int ArgParser::getInt(const std::string &name) const {
|
||||
auto it = int_values_.find(name);
|
||||
if (it == int_values_.end()) {
|
||||
return 0;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::string ArgParser::helpText() const {
|
||||
std::ostringstream oss;
|
||||
oss << "Usage: " << program_name_ << " [options]\n\n";
|
||||
oss << "Options:\n";
|
||||
oss << " -h, --help Show this help\n";
|
||||
for (const auto &key : order_) {
|
||||
auto m = meta_.find(key);
|
||||
if (m == meta_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
oss << " --" << key << " <value>";
|
||||
if (m->second.required) {
|
||||
oss << " (required)";
|
||||
}
|
||||
oss << "\n";
|
||||
oss << " " << m->second.help;
|
||||
if (m->second.type == OptionType::kString) {
|
||||
auto s = string_values_.find(key);
|
||||
if (s != string_values_.end()) {
|
||||
oss << " [default: '" << s->second << "']";
|
||||
}
|
||||
} else {
|
||||
auto iv = int_values_.find(key);
|
||||
if (iv != int_values_.end()) {
|
||||
oss << " [default: " << iv->second << "]";
|
||||
}
|
||||
}
|
||||
oss << "\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
63
tools/argparse.hpp
Normal file
63
tools/argparse.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
struct StringOption {
|
||||
std::string name;
|
||||
std::string default_value;
|
||||
std::string help;
|
||||
bool required;
|
||||
};
|
||||
|
||||
struct IntOption {
|
||||
std::string name;
|
||||
int default_value;
|
||||
std::string help;
|
||||
bool required;
|
||||
};
|
||||
|
||||
explicit ArgParser(std::string program_name);
|
||||
|
||||
void addString(const std::string &name,
|
||||
const std::string &default_value,
|
||||
const std::string &help,
|
||||
bool required = false);
|
||||
|
||||
void addInt(const std::string &name,
|
||||
int default_value,
|
||||
const std::string &help,
|
||||
bool required = false);
|
||||
|
||||
bool parse(int argc, char **argv, std::string *error);
|
||||
|
||||
bool has(const std::string &name) const;
|
||||
std::string getString(const std::string &name) const;
|
||||
int getInt(const std::string &name) const;
|
||||
|
||||
std::string helpText() const;
|
||||
|
||||
private:
|
||||
enum class OptionType {
|
||||
kString,
|
||||
kInt
|
||||
};
|
||||
|
||||
struct OptionMeta {
|
||||
OptionType type;
|
||||
std::string help;
|
||||
bool required;
|
||||
};
|
||||
|
||||
std::string program_name_;
|
||||
std::vector<std::string> order_;
|
||||
std::unordered_map<std::string, OptionMeta> meta_;
|
||||
|
||||
std::unordered_map<std::string, std::string> string_values_;
|
||||
std::unordered_map<std::string, int> int_values_;
|
||||
std::unordered_map<std::string, bool> provided_;
|
||||
};
|
||||
276
tools/digilent_jtag.cpp
Normal file
276
tools/digilent_jtag.cpp
Normal file
@@ -0,0 +1,276 @@
|
||||
#include "digilent_jtag.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include <digilent/adept/dmgr.h>
|
||||
#include <digilent/adept/djtg.h>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kDefaultIrBits = 6;
|
||||
|
||||
std::string ercToString(ERC erc) {
|
||||
char code[cchErcMax] = {0};
|
||||
char msg[cchErcMsgMax] = {0};
|
||||
if (DmgrSzFromErc(erc, code, msg)) {
|
||||
return std::string(code) + ": " + msg;
|
||||
}
|
||||
return "ERC=" + std::to_string(erc);
|
||||
}
|
||||
|
||||
inline uint8_t getBit(const uint8_t* packed_bits, int bit_idx) {
|
||||
return static_cast<uint8_t>((packed_bits[bit_idx / 8] >> (bit_idx % 8)) & 0x1u);
|
||||
}
|
||||
|
||||
inline void setBit(uint8_t* packed_bits, int bit_idx, uint8_t bit) {
|
||||
const uint8_t mask = static_cast<uint8_t>(1u << (bit_idx % 8));
|
||||
if (bit & 0x1u) {
|
||||
packed_bits[bit_idx / 8] |= mask;
|
||||
} else {
|
||||
packed_bits[bit_idx / 8] &= static_cast<uint8_t>(~mask);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DigilentJtag::DigilentJtag() : hif_(hifInvalid), enabled_port_(-1), last_error_() {}
|
||||
|
||||
DigilentJtag::~DigilentJtag() { close(); }
|
||||
|
||||
bool DigilentJtag::open(int port) {
|
||||
close();
|
||||
|
||||
int count = 0;
|
||||
if (!DmgrEnumDevices(&count)) {
|
||||
return setErrorFromDmgr("DmgrEnumDevices");
|
||||
}
|
||||
if (count <= 0) {
|
||||
return setError("open: no Digilent devices found");
|
||||
}
|
||||
|
||||
DVC dvc{};
|
||||
if (!DmgrGetDvc(0, &dvc)) {
|
||||
return setErrorFromDmgr("DmgrGetDvc");
|
||||
}
|
||||
|
||||
return open(std::string(dvc.szConn), port);
|
||||
}
|
||||
|
||||
bool DigilentJtag::open(const std::string& selector, int port) {
|
||||
close();
|
||||
|
||||
if (selector.empty()) {
|
||||
return setError("open: selector is empty");
|
||||
}
|
||||
|
||||
std::vector<char> sel(selector.begin(), selector.end());
|
||||
sel.push_back('\0');
|
||||
|
||||
if (!DmgrOpen(&hif_, sel.data())) {
|
||||
hif_ = hifInvalid;
|
||||
return setErrorFromDmgr("DmgrOpen");
|
||||
}
|
||||
|
||||
if (!DjtgEnableEx(hif_, static_cast<INT32>(port))) {
|
||||
if (!DjtgEnable(hif_)) {
|
||||
DmgrClose(hif_);
|
||||
hif_ = hifInvalid;
|
||||
return setErrorFromDmgr("DjtgEnableEx/DjtgEnable");
|
||||
}
|
||||
enabled_port_ = 0;
|
||||
} else {
|
||||
enabled_port_ = port;
|
||||
}
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void DigilentJtag::close() {
|
||||
if (hif_ != hifInvalid) {
|
||||
(void)DjtgDisable(hif_);
|
||||
(void)DmgrClose(hif_);
|
||||
}
|
||||
hif_ = hifInvalid;
|
||||
enabled_port_ = -1;
|
||||
}
|
||||
|
||||
bool DigilentJtag::isOpen() const { return hif_ != hifInvalid; }
|
||||
|
||||
HIF DigilentJtag::handle() const { return hif_; }
|
||||
|
||||
bool DigilentJtag::setSpeed(uint32_t requested_hz, uint32_t* actual_hz) {
|
||||
if (!isOpen()) {
|
||||
return setError("setSpeed: device not open");
|
||||
}
|
||||
|
||||
DWORD actual = 0;
|
||||
if (!DjtgSetSpeed(hif_, static_cast<DWORD>(requested_hz), &actual)) {
|
||||
return setErrorFromDmgr("DjtgSetSpeed");
|
||||
}
|
||||
if (actual_hz) {
|
||||
*actual_hz = static_cast<uint32_t>(actual);
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::setChain(int chain, int ir_bits) {
|
||||
uint32_t opcode = 0;
|
||||
if (chain == 1) {
|
||||
opcode = 0x02; // USER1 on Spartan-6
|
||||
} else if (chain == 2) {
|
||||
opcode = 0x03; // USER2 on Spartan-6
|
||||
} else {
|
||||
return setError("setChain: unsupported chain index (expected 1 or 2)");
|
||||
}
|
||||
return setInstruction(opcode, ir_bits);
|
||||
}
|
||||
|
||||
bool DigilentJtag::setInstruction(uint32_t instruction, int ir_bits) {
|
||||
if (!isOpen()) {
|
||||
return setError("setInstruction: device not open");
|
||||
}
|
||||
if (ir_bits <= 0 || ir_bits > 64) {
|
||||
return setError("setInstruction: ir_bits out of range");
|
||||
}
|
||||
|
||||
// Force Test-Logic-Reset, then RTI.
|
||||
uint8_t tlr = 0x3f; // 6 ones
|
||||
if (!putTmsBits(&tlr, 6)) return false;
|
||||
uint8_t rti = 0x00; // one zero
|
||||
if (!putTmsBits(&rti, 1)) return false;
|
||||
|
||||
// RTI -> Shift-IR : 1,1,0,0 (LSB-first 0b0011)
|
||||
uint8_t to_shift_ir = 0x03;
|
||||
if (!putTmsBits(&to_shift_ir, 4)) return false;
|
||||
|
||||
std::vector<uint8_t> tx(static_cast<size_t>((ir_bits + 7) / 8), 0);
|
||||
for (int i = 0; i < ir_bits && i < 64; ++i) {
|
||||
if ((instruction >> i) & 0x1u) {
|
||||
tx[static_cast<size_t>(i / 8)] |= static_cast<uint8_t>(1u << (i % 8));
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> rx(tx.size(), 0);
|
||||
|
||||
if (ir_bits > 1) {
|
||||
if (!putTdiBits(false, tx.data(), rx.data(), ir_bits - 1)) return false;
|
||||
}
|
||||
const uint8_t last_tx = getBit(tx.data(), ir_bits - 1);
|
||||
uint8_t last_rx = 0;
|
||||
if (!putTdiBits(true, &last_tx, &last_rx, 1)) return false;
|
||||
|
||||
// Exit1-IR -> Update-IR -> Idle: 1,0
|
||||
uint8_t to_idle = 0x01;
|
||||
if (!putTmsBits(&to_idle, 2)) return false;
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::shiftData(const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count) {
|
||||
if (!isOpen()) {
|
||||
return setError("shiftData: device not open");
|
||||
}
|
||||
if (bit_count <= 0) {
|
||||
return setError("shiftData: bit_count must be > 0");
|
||||
}
|
||||
|
||||
const size_t nbytes = static_cast<size_t>((bit_count + 7) / 8);
|
||||
std::vector<uint8_t> tx_zeros;
|
||||
if (!tx_bits) {
|
||||
tx_zeros.assign(nbytes, 0);
|
||||
tx_bits = tx_zeros.data();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> rx_tmp;
|
||||
if (!rx_bits) {
|
||||
rx_tmp.assign(nbytes, 0);
|
||||
rx_bits = rx_tmp.data();
|
||||
} else {
|
||||
std::memset(rx_bits, 0, nbytes);
|
||||
}
|
||||
|
||||
if (!enterShiftDR()) return false;
|
||||
|
||||
if (bit_count > 1) {
|
||||
if (!putTdiBits(false, tx_bits, rx_bits, bit_count - 1)) return false;
|
||||
}
|
||||
|
||||
const uint8_t tx_last = getBit(tx_bits, bit_count - 1);
|
||||
uint8_t rx_last = 0;
|
||||
if (!putTdiBits(true, &tx_last, &rx_last, 1)) return false;
|
||||
setBit(rx_bits, bit_count - 1, rx_last & 0x1u);
|
||||
|
||||
if (!leaveShiftToIdle()) return false;
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::shiftData(const std::vector<uint8_t>& tx_bits, std::vector<uint8_t>* rx_bits, int bit_count) {
|
||||
if (bit_count <= 0) {
|
||||
return setError("shiftData(vector): bit_count must be > 0");
|
||||
}
|
||||
const size_t nbytes = static_cast<size_t>((bit_count + 7) / 8);
|
||||
if (tx_bits.size() < nbytes) {
|
||||
return setError("shiftData(vector): tx_bits is smaller than required bit_count");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> local_rx;
|
||||
local_rx.assign(nbytes, 0);
|
||||
|
||||
if (!shiftData(tx_bits.data(), local_rx.data(), bit_count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rx_bits) {
|
||||
*rx_bits = std::move(local_rx);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string& DigilentJtag::lastError() const { return last_error_; }
|
||||
|
||||
bool DigilentJtag::enterShiftDR() {
|
||||
// Idle -> Select-DR -> Capture-DR -> Shift-DR : 1,0,0
|
||||
uint8_t to_shift_dr = 0x01;
|
||||
return putTmsBits(&to_shift_dr, 3);
|
||||
}
|
||||
|
||||
bool DigilentJtag::leaveShiftToIdle() {
|
||||
// Exit1-DR -> Update-DR -> Idle : 1,0
|
||||
uint8_t to_idle = 0x01;
|
||||
return putTmsBits(&to_idle, 2);
|
||||
}
|
||||
|
||||
bool DigilentJtag::putTmsBits(const uint8_t* tms_bits, int bit_count) {
|
||||
if (!DjtgPutTmsBits(hif_, fFalse, const_cast<uint8_t*>(tms_bits), nullptr, static_cast<DWORD>(bit_count), fFalse)) {
|
||||
return setErrorFromDmgr("DjtgPutTmsBits");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::putTdiBits(bool tms, const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count) {
|
||||
if (!DjtgPutTdiBits(
|
||||
hif_,
|
||||
tms ? fTrue : fFalse,
|
||||
const_cast<uint8_t*>(tx_bits),
|
||||
rx_bits,
|
||||
static_cast<DWORD>(bit_count),
|
||||
fFalse)) {
|
||||
return setErrorFromDmgr("DjtgPutTdiBits");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DigilentJtag::setError(const std::string& msg) {
|
||||
last_error_ = msg;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DigilentJtag::setErrorFromDmgr(const std::string& where) {
|
||||
last_error_ = where + " failed: " + ercToString(DmgrGetLastError());
|
||||
return false;
|
||||
}
|
||||
52
tools/digilent_jtag.hpp
Normal file
52
tools/digilent_jtag.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <digilent/adept/dpcdecl.h>
|
||||
|
||||
class DigilentJtag {
|
||||
public:
|
||||
DigilentJtag();
|
||||
~DigilentJtag();
|
||||
|
||||
DigilentJtag(const DigilentJtag&) = delete;
|
||||
DigilentJtag& operator=(const DigilentJtag&) = delete;
|
||||
|
||||
bool open(int port = 0);
|
||||
bool open(const std::string& selector, int port = 0);
|
||||
void close();
|
||||
|
||||
bool isOpen() const;
|
||||
HIF handle() const;
|
||||
|
||||
bool setSpeed(uint32_t requested_hz, uint32_t* actual_hz = nullptr);
|
||||
|
||||
// For Spartan-6 style USER chains:
|
||||
// chain=1 -> USER1 opcode 0x02, chain=2 -> USER2 opcode 0x03
|
||||
bool setChain(int chain, int ir_bits = 6);
|
||||
|
||||
bool setInstruction(uint32_t instruction, int ir_bits);
|
||||
|
||||
// Shifts one DR transaction from Idle -> ShiftDR -> UpdateDR -> Idle.
|
||||
// tx_bits is LSB-first in packed byte form.
|
||||
// rx_bits, when non-null, receives captured TDO bits in the same packing.
|
||||
bool shiftData(const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count);
|
||||
|
||||
bool shiftData(const std::vector<uint8_t>& tx_bits, std::vector<uint8_t>* rx_bits, int bit_count);
|
||||
|
||||
const std::string& lastError() const;
|
||||
|
||||
private:
|
||||
bool enterShiftDR();
|
||||
bool leaveShiftToIdle();
|
||||
bool putTmsBits(const uint8_t* tms_bits, int bit_count);
|
||||
bool putTdiBits(bool tms, const uint8_t* tx_bits, uint8_t* rx_bits, int bit_count);
|
||||
bool setError(const std::string& msg);
|
||||
bool setErrorFromDmgr(const std::string& where);
|
||||
|
||||
HIF hif_;
|
||||
int enabled_port_;
|
||||
std::string last_error_;
|
||||
};
|
||||
90
tools/test.cpp
Normal file
90
tools/test.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "digilent_jtag.hpp"
|
||||
#include "argparse.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
void write(DigilentJtag &jtag, uint32_t addr, uint8_t data, bool reset){
|
||||
uint8_t d[6], din[6];
|
||||
d[0] = data;
|
||||
d[1] = (uint8_t)addr;
|
||||
d[2] = (uint8_t)(addr>>8);
|
||||
d[3] = (uint8_t)(addr>>16);
|
||||
d[4] = (uint8_t)(addr>>24);
|
||||
d[5] = ((reset) ? 1 : 0) | 0x2; // <we><rst>
|
||||
|
||||
jtag.shiftData(d, din, 42);
|
||||
}
|
||||
|
||||
uint8_t read(DigilentJtag &jtag, uint32_t addr, bool reset){
|
||||
uint8_t d[6], din[6];
|
||||
d[0] = 0xff;
|
||||
d[1] = (uint8_t)addr;
|
||||
d[2] = (uint8_t)(addr>>8);
|
||||
d[3] = (uint8_t)(addr>>16);
|
||||
d[4] = (uint8_t)(addr>>24);
|
||||
d[5] = ((reset) ? 1 : 0); // <we><rst>
|
||||
|
||||
jtag.shiftData(d, din, 42);
|
||||
// Read back
|
||||
jtag.shiftData(d, din, 42);
|
||||
|
||||
return din[0];
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
ArgParser parser(argc > 0 ? argv[0] : "test");
|
||||
parser.addString("write", "", "file to write");
|
||||
|
||||
std::string parse_error;
|
||||
if (!parser.parse(argc, argv, &parse_error)) {
|
||||
if (parse_error == "help") {
|
||||
std::printf("%s", parser.helpText().c_str());
|
||||
return 0;
|
||||
}
|
||||
std::printf("Argument error: %s\n\n", parse_error.c_str());
|
||||
std::printf("%s", parser.helpText().c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::string arg_write = parser.getString("write");
|
||||
|
||||
DigilentJtag jtag;
|
||||
if(!jtag.open()){
|
||||
printf("Could not open programmer\r\n");
|
||||
return -1;
|
||||
}
|
||||
jtag.setChain(1);
|
||||
|
||||
// Start reset
|
||||
read(jtag, 0, true);
|
||||
|
||||
if(arg_write!=""){
|
||||
uint32_t addr = 0;
|
||||
uint8_t buf[32];
|
||||
int nr;
|
||||
FILE* f = fopen(arg_write.c_str(), "rb");
|
||||
if(!f){
|
||||
goto end;
|
||||
}
|
||||
|
||||
do{
|
||||
nr = fread(buf, 1, 32, f);
|
||||
for(int i=0; i<32; i++){
|
||||
write(jtag, addr, buf[i], true);
|
||||
addr++;
|
||||
}
|
||||
}while(nr>0);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
end:
|
||||
// End reset
|
||||
read(jtag, 0, false);
|
||||
|
||||
jtag.close();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user