4 Commits

Author SHA1 Message Date
105dbed8e4 Added back in the jtag bridge
Now talking over the bus instead of using dpram
2026-02-27 17:39:43 +01:00
6f680377db jtag memory selectable 2026-02-27 16:09:33 +01:00
3a9b2acf9e New wishbone-jtag bridge 2026-02-27 15:56:56 +01:00
838204653a TImer working with tests
TODO: think of other way of shifting in data. Bit errors make uploading difficult
2026-02-25 22:01:28 +01:00
28 changed files with 2228 additions and 702 deletions

View File

@@ -13,75 +13,42 @@ package = tqg144
speedgrade = -2 speedgrade = -2
files_def = boards/mimas_v1/ip/clk_gen.xco files_def = boards/mimas_v1/ip/clk_gen.xco
[target.synth] [target.tools]
toolchain = ISE toolchain = make
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh output_files = tools/test
family = spartan6 buildroot = tools
device = xc6slx9 files_makefile = tools/Makefile
package = tqg144 files_other = tools/digilent_jtag.cpp
speedgrade = -2 tools/digilent_jtag.hpp
toplevel = top_generic tools/argparse.cpp
xst_opts = -vlgincdir rtl/util tools/argparse.hpp
files_verilog = rtl/toplevel/top_generic.v tools/test.cpp
rtl/util/conv.vh
rtl/core/nco_q15.v # Testbenches
rtl/core/sigmadelta_sampler.v # -----------
rtl/core/sigmadelta_rcmodel_q15.v
rtl/core/sigmadelta_input_q15.v [target.tb_wb_timer]
rtl/core/mul_const.v toolchain = iverilog
rtl/core/lpf_iir_q15_k.v runtime = all
rtl/core/decimate_by_r_q15.v toplevel = tb_wb_timer
rtl/core/mcu_peripherals.v files_verilog = sim/tb/tb_wb_timer.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
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 rtl/wb/wb_timer.v
files_con = boards/mimas_v1/constraints.ucf [target.tb_cdc_strobe_data]
files_other = rtl/util/rc_alpha_q15.vh toolchain = iverilog
rtl/util/clog2.vh runtime = all
sw/sweep/sweep.hex toplevel = tb_cdc_strobe_data
files_verilog = sim/tb/tb_cdc_strobe_data.v
rtl/core/cdc_strobe_data.v
[target.tb_jtag_wb_bridge]
toolchain = iverilog
runtime = all
toplevel = tb_jtag_wb_bridge
files_verilog = sim/tb/tb_jtag_wb_bridge.v
rtl/wb/jtag_wb_bridge.v
rtl/core/cdc_req_resp.v
rtl/core/cdc_strobe_data.v
[target.synth_sim] [target.synth_sim]
toolchain = iverilog toolchain = iverilog
@@ -99,6 +66,8 @@ files_verilog = rtl/toplevel/top_generic.v
rtl/core/decimate_by_r_q15.v rtl/core/decimate_by_r_q15.v
rtl/core/mcu_peripherals.v rtl/core/mcu_peripherals.v
rtl/core/mcu.v rtl/core/mcu.v
rtl/core/cdc_strobe_data.v
rtl/core/cdc_req_resp.v
rtl/core/mem_jtag_writable.v rtl/core/mem_jtag_writable.v
# Arch # Arch
rtl/core/lvds_comparator.v rtl/core/lvds_comparator.v
@@ -152,6 +121,85 @@ files_other = rtl/util/rc_alpha_q15.vh
rtl/util/conv.vh rtl/util/conv.vh
sw/sweep/sweep.hex sw/sweep/sweep.hex
# Synth targets
# -------------
[target.synth]
toolchain = ISE
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
family = spartan6
device = xc6slx9
package = tqg144
speedgrade = -2
toplevel = top_generic
xst_opts = -vlgincdir rtl/util -keep_hierarchy yes
files_verilog = rtl/toplevel/top_generic.v
rtl/util/conv.vh
rtl/core/nco_q15.v
rtl/core/sigmadelta_sampler.v
rtl/core/sigmadelta_rcmodel_q15.v
rtl/core/sigmadelta_input_q15.v
rtl/core/mul_const.v
rtl/core/lpf_iir_q15_k.v
rtl/core/decimate_by_r_q15.v
rtl/core/mcu_peripherals.v
rtl/core/cdc_strobe_data.v
rtl/core/cdc_req_resp.v
rtl/core/mcu.v
rtl/core/arbiter.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
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
rtl/serv/serving_ram.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/wb_arbiter.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/sweep/sweep.hex
[target.jtag] [target.jtag]
toolchain = ISE toolchain = ISE
ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh
@@ -165,33 +213,8 @@ files_other =
files_con = boards/mimas_v1/constraints.ucf files_con = boards/mimas_v1/constraints.ucf
files_verilog = rtl/arch/spartan-6/jtag_if.v files_verilog = rtl/arch/spartan-6/jtag_if.v
rtl/arch/spartan-6/clk_gen.v rtl/arch/spartan-6/clk_gen.v
rtl/core/cdc_strobe_data.v
rtl/core/cdc_req_resp.v
rtl/wb/jtag_wb_bridge.v rtl/wb/jtag_wb_bridge.v
rtl/wb/wb_gpio.v rtl/wb/wb_gpio.v
rtl/toplevel/top_jtag.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

138
rtl/core/arbiter.v Normal file
View File

@@ -0,0 +1,138 @@
/**
* Module: arbiter
*
* Description:
* A look ahead, round-robing parameterized arbiter.
*
* <> request
* each bit is controlled by an actor and each actor can 'request' ownership
* of the shared resource by bring high its request bit.
*
* <> grant
* when an actor has been given ownership of shared resource its 'grant' bit
* is driven high
*
* <> select
* binary representation of the grant signal (optional use)
*
* <> active
* is brought high by the arbiter when (any) actor has been given ownership
* of shared resource.
*
*
* Created: Sat Jun 1 20:26:44 EDT 2013
*
* Author: Berin Martini // berin.martini@gmail.com
*/
`ifndef _arbiter_ `define _arbiter_
`include "../util/clog2.vh"
module arbiter
#(parameter
NUM_PORTS = 6,
SEL_WIDTH = ((NUM_PORTS > 1) ? `CLOG2(NUM_PORTS) : 1))
(input wire clk,
input wire rst,
input wire [NUM_PORTS-1:0] request,
output reg [NUM_PORTS-1:0] grant,
output reg [SEL_WIDTH-1:0] select,
output reg active
);
/**
* Local parameters
*/
localparam WRAP_LENGTH = 2*NUM_PORTS;
// Find First 1 - Start from MSB and count downwards, returns 0 when no
// bit set
function [SEL_WIDTH-1:0] ff1 (
input [NUM_PORTS-1:0] in
);
reg set;
integer i;
begin
set = 1'b0;
ff1 = 'b0;
for (i = 0; i < NUM_PORTS; i = i + 1) begin
if (in[i] & ~set) begin
set = 1'b1;
ff1 = i[0 +: SEL_WIDTH];
end
end
end
endfunction
`ifdef VERBOSE
initial $display("Bus arbiter with %d units", NUM_PORTS);
`endif
/**
* Internal signals
*/
integer yy;
wire next;
wire [NUM_PORTS-1:0] order;
reg [NUM_PORTS-1:0] token;
wire [NUM_PORTS-1:0] token_lookahead [NUM_PORTS-1:0];
wire [WRAP_LENGTH-1:0] token_wrap;
/**
* Implementation
*/
assign token_wrap = {token, token};
assign next = ~|(token & request);
always @(posedge clk)
grant <= token & request;
always @(posedge clk)
select <= ff1(token & request);
always @(posedge clk)
active <= |(token & request);
always @(posedge clk)
if (rst) token <= 'b1;
else if (next) begin
for (yy = 0; yy < NUM_PORTS; yy = yy + 1) begin : TOKEN_
if (order[yy]) begin
token <= token_lookahead[yy];
end
end
end
genvar xx;
generate
for (xx = 0; xx < NUM_PORTS; xx = xx + 1) begin : ORDER_
assign token_lookahead[xx] = token_wrap[xx +: NUM_PORTS];
assign order[xx] = |(token_lookahead[xx] & request);
end
endgenerate
endmodule
`endif // `ifndef _arbiter_

70
rtl/core/cdc_req_resp.v Normal file
View File

@@ -0,0 +1,70 @@
`timescale 1 ns/1 ps
// =============================================================================
// cdc_req_resp
// Bidirectional channel made from two cdc_strobe_data mailboxes.
// =============================================================================
module cdc_req_resp #(
parameter integer REQ_W = 32,
parameter integer RESP_W = 32,
parameter integer STABLE_SAMPLES = 2
)(
// Side A (e.g., JTAG/TCK)
input wire a_clk,
input wire a_rst,
input wire a_req_pulse,
input wire [REQ_W-1:0] a_req_data,
output wire a_req_busy,
output wire a_req_accepted,
output wire a_resp_pulse,
output wire [RESP_W-1:0] a_resp_data,
// Side B (e.g., system/i_clk)
input wire b_clk,
input wire b_rst,
output wire b_req_pulse,
output wire [REQ_W-1:0] b_req_data,
input wire b_resp_pulse,
input wire [RESP_W-1:0] b_resp_data,
output wire b_resp_busy,
output wire b_resp_accepted
);
cdc_strobe_data #(
.WIDTH(REQ_W),
.STABLE_SAMPLES(STABLE_SAMPLES)
) u_req (
.s_clk(a_clk),
.s_rst(a_rst),
.s_pulse(a_req_pulse),
.s_data(a_req_data),
.s_busy(a_req_busy),
.s_accepted(a_req_accepted),
.d_clk(b_clk),
.d_rst(b_rst),
.d_pulse(b_req_pulse),
.d_data(b_req_data)
);
cdc_strobe_data #(
.WIDTH(RESP_W),
.STABLE_SAMPLES(STABLE_SAMPLES)
) u_resp (
.s_clk(b_clk),
.s_rst(b_rst),
.s_pulse(b_resp_pulse),
.s_data(b_resp_data),
.s_busy(b_resp_busy),
.s_accepted(b_resp_accepted),
.d_clk(a_clk),
.d_rst(a_rst),
.d_pulse(a_resp_pulse),
.d_data(a_resp_data)
);
endmodule

130
rtl/core/cdc_strobe_data.v Normal file
View File

@@ -0,0 +1,130 @@
`timescale 1 ns/1 ps
// =============================================================================
// cdc_strobe_data
// - One-deep mailbox for (strobe + data) crossing clock domains.
// - Uses toggle req/ack with 2FF sync for toggles.
// - Wide bus is held stable by source until ack, destination samples-until-stable.
// =============================================================================
module cdc_strobe_data #(
parameter integer WIDTH = 32,
parameter integer STABLE_SAMPLES = 2 // >=2 recommended
)(
// Source domain
input wire s_clk,
input wire s_rst, // async OK (posedge) if used consistently
input wire s_pulse, // strobe (1+ cycles). Accepted when not busy.
input wire [WIDTH-1:0] s_data,
output wire s_busy, // 1 = mailbox full / waiting for ack
output wire s_accepted, // 1-cycle pulse when we accepted s_pulse
// Destination domain
input wire d_clk,
input wire d_rst,
output reg d_pulse, // 1-cycle pulse on new data
output reg [WIDTH-1:0] d_data // updated when d_pulse asserted; held otherwise
);
// ----------------------------
// Source: hold + req toggle
// ----------------------------
reg [WIDTH-1:0] s_hold;
reg s_req_tog;
reg s_inflight;
// Ack toggle synchronized into source domain
(* ASYNC_REG="TRUE" *) reg s_ack_sync1, s_ack_sync2;
assign s_busy = s_inflight;
wire do_accept = s_pulse && !s_inflight;
assign s_accepted = do_accept;
// d_ack_tog is generated in destination domain (declared below as reg)
// and is synced here with 2FF.
always @(posedge s_clk or posedge s_rst) begin
if (s_rst) begin
s_hold <= {WIDTH{1'b0}};
s_req_tog <= 1'b0;
s_inflight <= 1'b0;
s_ack_sync1 <= 1'b0;
s_ack_sync2 <= 1'b0;
end else begin
s_ack_sync1 <= d_ack_tog;
s_ack_sync2 <= s_ack_sync1;
// clear inflight when ack matches current req toggle
if (s_inflight && (s_ack_sync2 == s_req_tog))
s_inflight <= 1'b0;
// accept new item
if (do_accept) begin
s_hold <= s_data;
s_req_tog <= ~s_req_tog;
s_inflight <= 1'b1;
end
end
end
// ----------------------------
// Destination: sync req toggle, sample-until-stable, then ack toggle
// ----------------------------
(* ASYNC_REG="TRUE" *) reg d_req_sync1, d_req_sync2;
reg d_req_seen;
reg d_ack_tog;
reg [WIDTH-1:0] samp;
reg [WIDTH-1:0] samp_prev;
integer stable_cnt;
reg capturing;
wire d_new_req = (d_req_sync2 != d_req_seen);
always @(posedge d_clk or posedge d_rst) begin
if (d_rst) begin
d_req_sync1 <= 1'b0;
d_req_sync2 <= 1'b0;
d_req_seen <= 1'b0;
d_ack_tog <= 1'b0;
d_pulse <= 1'b0;
d_data <= {WIDTH{1'b0}};
samp <= {WIDTH{1'b0}};
samp_prev <= {WIDTH{1'b0}};
stable_cnt <= 0;
capturing <= 1'b0;
end else begin
d_pulse <= 1'b0;
d_req_sync1 <= s_req_tog;
d_req_sync2 <= d_req_sync1;
if (d_new_req && !capturing) begin
capturing <= 1'b1;
stable_cnt <= 0;
samp_prev <= s_hold;
samp <= s_hold;
end else if (capturing) begin
samp <= s_hold;
if (samp == samp_prev) begin
if (stable_cnt < (STABLE_SAMPLES-1))
stable_cnt <= stable_cnt + 1;
else begin
// accept
d_data <= samp;
d_pulse <= 1'b1;
d_req_seen <= d_req_sync2;
d_ack_tog <= ~d_ack_tog;
capturing <= 1'b0;
end
end else begin
stable_cnt <= 0;
end
samp_prev <= samp;
end
end
end
endmodule

View File

@@ -4,7 +4,8 @@
module mcu #( module mcu #(
parameter memfile = "", parameter memfile = "",
parameter memsize = 8192, parameter memsize = 8192,
parameter sim = 1'b0 parameter sim = 1'b0,
parameter jtag = 1
)( )(
input wire i_clk, input wire i_clk,
input wire i_rst, input wire i_rst,
@@ -16,29 +17,39 @@ module mcu #(
output wire [31:0] o_GPO_A, output wire [31:0] o_GPO_A,
output wire [31:0] o_GPO_B, output wire [31:0] o_GPO_B,
output wire [31:0] o_GPO_C, output wire [31:0] o_GPO_C,
output wire [31:0] o_GPO_D, output wire [31:0] o_GPO_D
output wire o_test
); );
localparam WITH_CSR = 1; localparam WITH_CSR = 1;
localparam regs = 32+WITH_CSR*4;
localparam rf_width = 8; localparam rf_width = 8;
wire rst; wire rst;
wire rst_mem_reason; wire rst_wb;
wire rst_mem_peripherals;
wire rst_cmd_jtag;
wire timer_irq; wire timer_irq;
assign rst = i_rst | rst_mem_reason; assign rst = i_rst | rst_mem_peripherals | rst_cmd_jtag;
// Keep the Wishbone path alive during JTAG "core reset" so memory can be programmed.
assign o_test = timer_irq; assign rst_wb = i_rst;
// Busses // Busses
// CPU->memory // CPU<->memory interconnect (CPU is a WB master)
wire [31:0] wb_mem_adr; wire [31:0] wb_mem_adr;
wire [31:0] wb_mem_dat; wire [31:0] wb_mem_dat;
wire [3:0] wb_mem_sel; wire [3:0] wb_mem_sel;
wire wb_mem_we; wire wb_mem_we;
wire wb_mem_stb; wire wb_mem_stb;
wire [31:0] wb_mem_rdt; wire [31:0] wb_mem_rdt_cpu;
wire wb_mem_ack; wire wb_mem_ack_cpu;
// Interconnect->memory (shared WB slave side)
wire [31:0] wb_mem_adr_s;
wire [31:0] wb_mem_dat_s;
wire [3:0] wb_mem_sel_s;
wire wb_mem_we_s;
wire wb_mem_stb_s;
wire [31:0] wb_mem_rdt_s;
wire wb_mem_ack_s;
// CPU->peripherals // CPU->peripherals
wire [31:0] wb_ext_adr; wire [31:0] wb_ext_adr;
wire [31:0] wb_ext_dat; wire [31:0] wb_ext_dat;
@@ -47,20 +58,6 @@ module mcu #(
wire wb_ext_stb; wire wb_ext_stb;
wire [31:0] wb_ext_rdt; wire [31:0] wb_ext_rdt;
wire wb_ext_ack; 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 // GPIO
wire [4*32-1:0] GPO; wire [4*32-1:0] GPO;
@@ -74,21 +71,14 @@ module mcu #(
assign GPI[32*3-1:32*2] = i_GPI_C; assign GPI[32*3-1:32*2] = i_GPI_C;
assign GPI[32*4-1:32*3] = i_GPI_D; assign GPI[32*4-1:32*3] = i_GPI_D;
// SERV core with mux splitting dbus into mem and ext and cpu #(
// 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), .sim(sim),
.with_csr(WITH_CSR), .WITH_CSR(WITH_CSR),
.with_c(0), .rf_width(rf_width)
.with_mdu(0) ) cpu (
) servile (
.i_clk(i_clk), .i_clk(i_clk),
.i_rst(rst), .i_rst(rst),
.i_timer_irq(1'b0), //timer_irq), .i_timer_irq(timer_irq),
//Memory interface //Memory interface
.o_wb_mem_adr(wb_mem_adr), .o_wb_mem_adr(wb_mem_adr),
@@ -96,8 +86,8 @@ module mcu #(
.o_wb_mem_sel(wb_mem_sel), .o_wb_mem_sel(wb_mem_sel),
.o_wb_mem_we(wb_mem_we), .o_wb_mem_we(wb_mem_we),
.o_wb_mem_stb(wb_mem_stb), .o_wb_mem_stb(wb_mem_stb),
.i_wb_mem_rdt(wb_mem_rdt), .i_wb_mem_rdt(wb_mem_rdt_cpu),
.i_wb_mem_ack(wb_mem_ack), .i_wb_mem_ack(wb_mem_ack_cpu),
//Extension interface //Extension interface
.o_wb_ext_adr(wb_ext_adr), .o_wb_ext_adr(wb_ext_adr),
@@ -106,62 +96,122 @@ module mcu #(
.o_wb_ext_we(wb_ext_we), .o_wb_ext_we(wb_ext_we),
.o_wb_ext_stb(wb_ext_stb), .o_wb_ext_stb(wb_ext_stb),
.i_wb_ext_rdt(wb_ext_rdt), .i_wb_ext_rdt(wb_ext_rdt),
.i_wb_ext_ack(wb_ext_ack), .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 generate
// Last 128 bytes are used for registers if (jtag) begin : gen_jtag_wb
servile_rf_mem_if #( wire [31:0] wb_jtag_adr;
.depth(memsize), wire [31:0] wb_jtag_dat;
.rf_regs(regs) wire [3:0] wb_jtag_sel;
) rf_mem_if ( wire wb_jtag_we;
.i_clk (i_clk), wire wb_jtag_cyc;
.i_rst (rst), wire wb_jtag_stb;
wire [31:0] wb_jtag_rdt;
wire wb_jtag_ack;
.i_waddr(rf_waddr), wire [2*32-1:0] wbm_adr_i;
.i_wdata(rf_wdata), wire [2*32-1:0] wbm_dat_i;
.i_wen(rf_wen), wire [2*4-1:0] wbm_sel_i;
.i_raddr(rf_raddr), wire [1:0] wbm_we_i;
.o_rdata(rf_rdata), wire [1:0] wbm_cyc_i;
.i_ren(rf_ren), wire [1:0] wbm_stb_i;
wire [2*3-1:0] wbm_cti_i;
wire [2*2-1:0] wbm_bte_i;
wire [2*32-1:0] wbm_dat_o;
wire [1:0] wbm_ack_o;
wire [1:0] wbm_err_o;
wire [1:0] wbm_rty_o;
.o_sram_waddr(sram_waddr), assign wbm_adr_i = {wb_jtag_adr, wb_mem_adr};
.o_sram_wdata(sram_wdata), assign wbm_dat_i = {wb_jtag_dat, wb_mem_dat};
.o_sram_wen(sram_wen), assign wbm_sel_i = {wb_jtag_sel, wb_mem_sel};
.o_sram_raddr(sram_raddr), assign wbm_we_i = {wb_jtag_we, wb_mem_we};
.i_sram_rdata(sram_rdata), assign wbm_cyc_i = {wb_jtag_cyc, wb_mem_stb};
// .o_sram_ren(sram_ren), assign wbm_stb_i = {wb_jtag_stb, wb_mem_stb};
assign wbm_cti_i = 6'b0;
assign wbm_bte_i = 4'b0;
.i_wb_adr(wb_mem_adr[`CLOG2(memsize)-1:2]), assign wb_mem_rdt_cpu = wbm_dat_o[31:0];
.i_wb_stb(wb_mem_stb), assign wb_mem_ack_cpu = wbm_ack_o[0];
.i_wb_we(wb_mem_we) , assign wb_jtag_rdt = wbm_dat_o[63:32];
.i_wb_sel(wb_mem_sel), assign wb_jtag_ack = wbm_ack_o[1];
.i_wb_dat(wb_mem_dat),
.o_wb_rdt(wb_mem_rdt), wb_arbiter #(
.o_wb_ack(wb_mem_ack) .dw(32),
.aw(32),
.num_masters(2)
) wb_mem_arbiter (
.wb_clk_i(i_clk),
.wb_rst_i(rst_wb),
.wbm_adr_i(wbm_adr_i),
.wbm_dat_i(wbm_dat_i),
.wbm_sel_i(wbm_sel_i),
.wbm_we_i(wbm_we_i),
.wbm_cyc_i(wbm_cyc_i),
.wbm_stb_i(wbm_stb_i),
.wbm_cti_i(wbm_cti_i),
.wbm_bte_i(wbm_bte_i),
.wbm_dat_o(wbm_dat_o),
.wbm_ack_o(wbm_ack_o),
.wbm_err_o(wbm_err_o),
.wbm_rty_o(wbm_rty_o),
.wbs_adr_o(wb_mem_adr_s),
.wbs_dat_o(wb_mem_dat_s),
.wbs_sel_o(wb_mem_sel_s),
.wbs_we_o(wb_mem_we_s),
.wbs_cyc_o(),
.wbs_stb_o(wb_mem_stb_s),
.wbs_cti_o(),
.wbs_bte_o(),
.wbs_dat_i(wb_mem_rdt_s),
.wbs_ack_i(wb_mem_ack_s),
.wbs_err_i(1'b0),
.wbs_rty_i(1'b0)
); );
jtag_wb_bridge #(
.chain(1)
) jtag_wb (
.i_clk(i_clk),
.i_rst(i_rst),
.o_wb_adr(wb_jtag_adr),
.o_wb_dat(wb_jtag_dat),
.o_wb_sel(wb_jtag_sel),
.o_wb_we(wb_jtag_we),
.o_wb_cyc(wb_jtag_cyc),
.o_wb_stb(wb_jtag_stb),
.i_wb_rdt(wb_jtag_rdt),
.i_wb_ack(wb_jtag_ack),
.o_cmd_reset(rst_cmd_jtag)
);
end else begin : gen_no_jtag_wb
assign wb_mem_adr_s = wb_mem_adr;
assign wb_mem_dat_s = wb_mem_dat;
assign wb_mem_sel_s = wb_mem_sel;
assign wb_mem_we_s = wb_mem_we;
assign wb_mem_stb_s = wb_mem_stb;
assign wb_mem_rdt_cpu = wb_mem_rdt_s;
assign wb_mem_ack_cpu = wb_mem_ack_s;
assign rst_cmd_jtag = 1'b0;
end
endgenerate
memory #( memory #(
.memfile(memfile), .memfile(memfile),
.depth(memsize), .memsize(memsize),
.sim(sim) .sim(sim)
) mem ( ) memory (
.i_clk(i_clk), .i_clk(i_clk),
.i_rst(i_rst), .i_rst(i_rst),
.i_waddr(sram_waddr), .i_wb_rst(rst_wb),
.i_wdata(sram_wdata), .i_wb_adr(wb_mem_adr_s),
.i_wen(sram_wen), .i_wb_dat(wb_mem_dat_s),
.i_raddr(sram_raddr), .i_wb_sel(wb_mem_sel_s),
.o_rdata(sram_rdata), .i_wb_we(wb_mem_we_s),
.o_core_reset(rst_mem_reason) .i_wb_stb(wb_mem_stb_s),
.o_wb_rdt(wb_mem_rdt_s),
.o_wb_ack(wb_mem_ack_s)
); );
mcu_peripherals peripherals ( mcu_peripherals peripherals (
@@ -177,7 +227,156 @@ module mcu #(
// Peripheral IO // Peripheral IO
.i_gpio(GPI), .i_gpio(GPI),
.o_gpio(GPO), .o_gpio(GPO),
.o_timer_irq(timer_irq) .o_timer_irq(timer_irq),
.o_core_reset(rst_mem_peripherals)
); );
endmodule endmodule
module cpu #(
parameter sim = 1'b0,
parameter WITH_CSR = 1,
parameter rf_width = 8
)(
input wire i_clk,
input wire i_rst,
input wire i_timer_irq,
// CPU->memory
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,
// CPU->peripherals
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 [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;
// SERV core with mux splitting dbus into mem and ext and
// arbiter combining mem and ibus.
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(i_rst),
.i_timer_irq(i_timer_irq),
.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),
.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),
.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)
);
serv_rf_ram #(
.width(rf_width),
.csr_regs(WITH_CSR*4)
) rf_ram (
.i_clk(i_clk),
.i_waddr(rf_waddr),
.i_wdata(rf_wdata),
.i_wen(rf_wen),
.i_raddr(rf_raddr),
.i_ren(rf_ren),
.o_rdata(rf_rdata)
);
endmodule
module memory #(
parameter memfile = "",
parameter memsize = 8192,
parameter sim = 1'b0
)(
input wire i_clk,
input wire i_rst,
input wire i_wb_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
);
localparam mem_depth = memsize/4;
localparam mem_aw = `CLOG2(mem_depth);
reg [31:0] mem [0:mem_depth-1] /* verilator public */;
reg [31:0] wb_rdt_r;
reg wb_ack_r;
wire [mem_aw-1:0] wb_word_adr = i_wb_adr[mem_aw+1:2];
assign o_wb_rdt = wb_rdt_r;
assign o_wb_ack = wb_ack_r;
always @(posedge i_clk) begin
if (i_rst || i_wb_rst) begin
wb_ack_r <= 1'b0;
wb_rdt_r <= 32'b0;
end else begin
wb_ack_r <= i_wb_stb & ~wb_ack_r;
if (i_wb_stb & ~wb_ack_r) begin
wb_rdt_r <= mem[wb_word_adr];
if (i_wb_we) begin
if (i_wb_sel[0]) mem[wb_word_adr][7:0] <= i_wb_dat[7:0];
if (i_wb_sel[1]) mem[wb_word_adr][15:8] <= i_wb_dat[15:8];
if (i_wb_sel[2]) mem[wb_word_adr][23:16] <= i_wb_dat[23:16];
if (i_wb_sel[3]) mem[wb_word_adr][31:24] <= i_wb_dat[31:24];
end
end
end
end
integer i;
initial begin
if (sim == 1'b1) begin
for (i = 0; i < mem_depth; i = i + 1)
mem[i] = 32'h00000000;
end
if (|memfile) begin
$display("Preloading %m from %s", memfile);
$readmemh(memfile, mem);
end
wb_rdt_r = 32'b0;
wb_ack_r = 1'b0;
end
endmodule

View File

@@ -14,13 +14,16 @@ module mcu_peripherals (
input wire [4*32-1:0] i_gpio, input wire [4*32-1:0] i_gpio,
output wire [4*32-1:0] o_gpio, output wire [4*32-1:0] o_gpio,
output wire o_timer_irq output wire o_timer_irq,
output wire o_core_reset
); );
localparam [31:0] GPIO_BASE_ADDR = 32'h4000_0000; localparam [31:0] GPIO_BASE_ADDR = 32'h4000_0000;
localparam [31:0] GPIO_ADDR_MASK = 32'hFFFF_0000; localparam [31:0] GPIO_ADDR_MASK = 32'hFFFF_0000;
localparam [31:0] TIMER_BASE_ADDR = 32'h4001_0000; localparam [31:0] TIMER_BASE_ADDR = 32'h4001_0000;
localparam [31:0] TIMER_ADDR_MASK = 32'hFFFF_0000; localparam [31:0] TIMER_ADDR_MASK = 32'hFFFF_0000;
assign o_core_reset = 1'b0;
wire [2*32-1:0] wbs_adr; wire [2*32-1:0] wbs_adr;
wire [2*32-1:0] wbs_dat_w; wire [2*32-1:0] wbs_dat_w;
wire [2*4-1:0] wbs_sel; wire [2*4-1:0] wbs_sel;

View File

@@ -1,6 +1,6 @@
`timescale 1ns/1ps `timescale 1ns/1ps
module memory #( module memory_jtag #(
parameter memfile = "", parameter memfile = "",
parameter depth = 256, parameter depth = 256,
parameter sim = 1'b0, parameter sim = 1'b0,

View File

@@ -23,6 +23,26 @@ module top_generic #(
.clk_out_15(clk_15) .clk_out_15(clk_15)
); );
// Reset conditioning for button input:
// - asynchronous assert when button is pressed (aresetn=0)
// - synchronous, debounced deassert in clk_15 domain
localparam [17:0] RESET_RELEASE_CYCLES = sim ? 18'd16 : 18'd150000; // ~10 ms @ 15 MHz on hardware
reg [17:0] rst_cnt = 18'd0;
reg sys_reset_r = 1'b1;
always @(posedge clk_15 or negedge aresetn) begin
if (!aresetn) begin
rst_cnt <= 18'd0;
sys_reset_r <= 1'b1;
end else if (sys_reset_r) begin
if (rst_cnt == RESET_RELEASE_CYCLES - 1'b1)
sys_reset_r <= 1'b0;
else
rst_cnt <= rst_cnt + 1'b1;
end
end
wire sys_reset = sys_reset_r;
wire sys_resetn = !sys_reset_r;
wire [31:0] GPIO_A; wire [31:0] GPIO_A;
wire [31:0] GPIO_B; wire [31:0] GPIO_B;
wire [31:0] GPIO_C; wire [31:0] GPIO_C;
@@ -32,10 +52,11 @@ module top_generic #(
mcu #( mcu #(
.memfile("../sw/sweep/sweep.hex"), .memfile("../sw/sweep/sweep.hex"),
.sim(sim) .sim(sim),
.jtag(1)
) mcu ( ) mcu (
.i_clk(clk_15), .i_clk(clk_15),
.i_rst(!aresetn), .i_rst(sys_reset),
.i_GPI_A(GPIO_A), .i_GPI_A(GPIO_A),
.i_GPI_B(GPIO_B), .i_GPI_B(GPIO_B),
.i_GPI_C(GPIO_C), .i_GPI_C(GPIO_C),
@@ -43,8 +64,7 @@ module top_generic #(
.o_GPO_A(GPIO_A), .o_GPO_A(GPIO_A),
.o_GPO_B(GPIO_B), .o_GPO_B(GPIO_B),
.o_GPO_C(GPIO_C), .o_GPO_C(GPIO_C),
.o_GPO_D(GPIO_D), .o_GPO_D(GPIO_D)
.o_test(test)
); );
@@ -55,7 +75,7 @@ module top_generic #(
.FS_HZ(80_000) .FS_HZ(80_000)
) nco ( ) nco (
.clk (clk_15), .clk (clk_15),
.rst_n (aresetn), .rst_n (sys_resetn),
.freq_hz(GPIO_A), .freq_hz(GPIO_A),
.sin_q15(sin_q15), .sin_q15(sin_q15),
.cos_q15(), .cos_q15(),
@@ -70,5 +90,5 @@ module top_generic #(
assign LED = GPIO_B[7:0]; assign LED = GPIO_B[7:0];
assign led_green = GPIO_C[0]; assign led_green = GPIO_C[0];
assign led_red = test; assign led_red = GPIO_C[1];
endmodule endmodule

View File

@@ -48,20 +48,18 @@ module top_jtag(
); );
wire [31:0] gpio; wire [31:0] gpio;
wire [31:0] gpio_in;
assign gpio_in = 32'h0;
wb_gpio #( wb_gpio #(
.address(32'h00000000) .address(32'h00000000)
) u_wb_gpio ( ) u_wb_gpio (
.i_wb_clk(clk_15), .i_wb_clk(clk_15),
.i_wb_rst(i_rst | cmd_reset), .i_wb_rst(i_rst),
.i_wb_adr(wb_adr), .i_wb_adr(wb_adr),
.i_wb_dat(wb_dat), .i_wb_dat(wb_dat),
.i_wb_sel(wb_sel), .i_wb_sel(wb_sel),
.i_wb_we(wb_we), .i_wb_we(wb_we),
.i_wb_stb(wb_stb & wb_cyc), .i_wb_stb(wb_stb & wb_cyc),
.i_gpio(gpio_in), .i_gpio(gpio),
.o_wb_rdt(wb_rdt), .o_wb_rdt(wb_rdt),
.o_wb_ack(wb_ack), .o_wb_ack(wb_ack),
.o_gpio(gpio) .o_gpio(gpio)
@@ -69,7 +67,7 @@ module top_jtag(
assign LED = gpio[7:0]; assign LED = gpio[7:0];
assign r2r = gpio[13:8]; assign r2r = gpio[13:8];
assign led_green = gpio[30]; assign led_green = cmd_reset;
assign led_red = gpio[31]; assign led_red = 'b0;
endmodule endmodule

View File

@@ -1,9 +1,9 @@
`timescale 1ns/1ps `timescale 1 ns/1 ps
module jtag_wb_bridge #( module jtag_wb_bridge #(
parameter integer chain = 1, parameter integer chain = 1,
// 0: Use cmd_addr[1:0] to select byte lane on 32-bit WB data bus. // 0: use addr[1:0] for byte lane on 32-bit WB
// 1: Always use lane 0 (LSB), for byte-wide memories that return data in [7:0]. // 1: always use lane 0
parameter integer byte_aligned = 0 parameter integer byte_aligned = 0
)( )(
input wire i_clk, input wire i_clk,
@@ -20,7 +20,10 @@ module jtag_wb_bridge #(
output wire o_cmd_reset output wire o_cmd_reset
); );
// JTAG interface wires
// ===========================================================================
// JTAG interface (Spartan-6 BSCAN wrapper)
// ===========================================================================
wire jtag_tck; wire jtag_tck;
wire jtag_tdi; wire jtag_tdi;
wire jtag_drck; wire jtag_drck;
@@ -31,14 +34,18 @@ module jtag_wb_bridge #(
wire jtag_reset; wire jtag_reset;
wire jtag_sel; wire jtag_sel;
reg [41:0] jtag_q; localparam integer JTAG_DR_W = 72;
wire [41:0] jtag_data_in;
wire jtag_async_reset; // 72-bit DR (symmetrical command/response)
// Command layout: [71:64] opcode, [63:32] addr, [31:0] data
// Response layout: [71:64] resp_seq, [63:56] status, [55:48] cmd_seq,
// [47:16] data, [15:8] flags, [7:0] last_op
reg [JTAG_DR_W-1:0] jtag_shreg;
jtag_if #( jtag_if #(
.chain(chain) .chain(chain)
) jtag ( ) u_jtag (
.i_tdo(jtag_q[0]), .i_tdo(jtag_shreg[0]),
.o_tck(jtag_tck), .o_tck(jtag_tck),
.o_tdi(jtag_tdi), .o_tdi(jtag_tdi),
.o_drck(jtag_drck), .o_drck(jtag_drck),
@@ -50,82 +57,103 @@ module jtag_wb_bridge #(
.o_sel(jtag_sel) .o_sel(jtag_sel)
); );
assign jtag_async_reset = jtag_reset || i_rst; wire jtag_async_reset = jtag_reset || i_rst;
// JTAG shift register behavior // ===========================================================================
always @(posedge jtag_drck or posedge jtag_async_reset) begin // CDC request/response channel (72/72 symmetric)
// Side A: JTAG/TCK domain
// Side B: system/i_clk domain
// ===========================================================================
wire a_req_busy;
wire a_req_accepted;
wire a_resp_pulse;
wire [JTAG_DR_W-1:0] a_resp_data;
wire b_req_pulse;
wire [JTAG_DR_W-1:0] b_req_data;
reg b_resp_pulse;
reg [JTAG_DR_W-1:0] b_resp_data;
wire b_resp_busy;
wire b_resp_accepted;
// Accept UPDATE as a request strobe (qualified by SEL and !busy)
wire a_req_pulse = jtag_sel && jtag_update && !a_req_busy;
wire [JTAG_DR_W-1:0] a_req_data = jtag_shreg;
cdc_req_resp #(
.REQ_W(JTAG_DR_W),
.RESP_W(JTAG_DR_W),
.STABLE_SAMPLES(2)
) u_cdc (
.a_clk(jtag_tck),
.a_rst(jtag_async_reset),
.a_req_pulse(a_req_pulse),
.a_req_data(a_req_data),
.a_req_busy(a_req_busy),
.a_req_accepted(a_req_accepted),
.a_resp_pulse(a_resp_pulse),
.a_resp_data(a_resp_data),
.b_clk(i_clk),
.b_rst(i_rst),
.b_req_pulse(b_req_pulse),
.b_req_data(b_req_data),
.b_resp_pulse(b_resp_pulse),
.b_resp_data(b_resp_data),
.b_resp_busy(b_resp_busy),
.b_resp_accepted(b_resp_accepted)
);
// ===========================================================================
// JTAG/TCK domain shift/capture
// ===========================================================================
reg [JTAG_DR_W-1:0] resp_hold_tck;
always @(posedge jtag_tck or posedge jtag_async_reset) begin
if (jtag_async_reset) begin if (jtag_async_reset) begin
jtag_q <= 42'b0; jtag_shreg <= {JTAG_DR_W{1'b0}};
end else if (jtag_sel && jtag_capture) begin resp_hold_tck <= {JTAG_DR_W{1'b0}};
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 end else begin
j_ack_sync_1 <= s_ack_tgl; // Latch new response word from CDC when it arrives (independent of CAPTURE)
j_ack_sync_2 <= j_ack_sync_1; if (a_resp_pulse) begin
resp_hold_tck <= a_resp_data;
end
if (jtag_sel && jtag_capture) begin
// Load response into shift register for host readout
jtag_shreg <= resp_hold_tck;
end else if (jtag_sel && jtag_shift) begin
// Shift: MSB in, LSB out to TDO
jtag_shreg <= {jtag_tdi, jtag_shreg[JTAG_DR_W-1:1]};
end
end end
end end
always @(posedge jtag_update or posedge jtag_async_reset) begin // ===========================================================================
if (jtag_async_reset) begin // System domain: Wishbone master + small command queue + response pending
j_cmd_hold <= 42'b0; // ===========================================================================
j_req_tgl <= 1'b0; // Opcodes
end else if (jtag_sel && (j_ack_sync_2 == j_req_tgl)) begin localparam [7:0] OP_NOP = 8'h00;
j_cmd_hold <= jtag_q; localparam [7:0] OP_RESET_ON = 8'h10;
j_req_tgl <= ~j_req_tgl; localparam [7:0] OP_RESET_OFF = 8'h11;
end localparam [7:0] OP_WRITE8 = 8'h20;
end localparam [7:0] OP_READ8 = 8'h21;
localparam [7:0] OP_WRITE32 = 8'h22;
localparam [7:0] OP_READ32 = 8'h23;
localparam [7:0] OP_PING = 8'h30;
localparam [7:0] OP_CLEAR_FLAGS = 8'h40;
// ----------------------------------------------------------------------------- // Wishbone regs
// Wishbone classic single-request master (1 outstanding transaction max).
// -----------------------------------------------------------------------------
reg wb_busy; reg wb_busy;
reg [31:0] wb_adr_r; reg [31:0] wb_adr_r;
reg [31:0] wb_dat_r; reg [31:0] wb_dat_r;
reg [3:0] wb_sel_r; reg [3:0] wb_sel_r;
reg wb_we_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_adr = wb_adr_r;
assign o_wb_dat = wb_dat_r; assign o_wb_dat = wb_dat_r;
@@ -133,64 +161,378 @@ module jtag_wb_bridge #(
assign o_wb_we = wb_we_r; assign o_wb_we = wb_we_r;
assign o_wb_cyc = wb_busy; assign o_wb_cyc = wb_busy;
assign o_wb_stb = wb_busy; assign o_wb_stb = wb_busy;
assign o_cmd_reset = cmd_reset_pulse_r;
// Reset control
reg cmd_reset_level_r;
assign o_cmd_reset = cmd_reset_level_r;
// For reporting only: sync a_req_busy (TCK domain) into i_clk
(* ASYNC_REG="TRUE" *) reg req_busy_sync1, req_busy_sync2;
wire req_busy_tck_sync = req_busy_sync2;
// Sequencing
reg [7:0] cmd_seq_r;
reg [7:0] resp_seq_r;
// Sticky flags (cleared by CLEAR_FLAGS or reset)
reg flag_cmd_overflow;
reg flag_illegal;
reg flag_wb_busy_at_req;
// Snapshot info
reg last_we_r;
reg [7:0] last_opcode_r;
// Active command / queued command
reg act_valid;
reg [7:0] act_opcode;
reg [31:0] act_addr;
reg [31:0] act_data;
reg [7:0] act_seq;
reg q_valid;
reg [7:0] q_opcode;
reg [31:0] q_addr;
reg [31:0] q_data;
reg [7:0] q_seq;
// Response pending buffer (to avoid dropping if resp mailbox busy)
reg resp_pending;
reg [JTAG_DR_W-1:0] resp_pending_word;
// Lane selection
wire [1:0] addr_lane = byte_aligned ? 2'b00 : act_addr[1:0];
// Helpers: form SEL/DAT for byte write
function [3:0] sel_from_lane(input [1:0] lane);
case (lane)
2'b00: sel_from_lane = 4'b0001;
2'b01: sel_from_lane = 4'b0010;
2'b10: sel_from_lane = 4'b0100;
default: sel_from_lane = 4'b1000;
endcase
endfunction
function [31:0] dat_from_lane_byte(input [1:0] lane, input [7:0] b);
case (lane)
2'b00: dat_from_lane_byte = {24'b0, b};
2'b01: dat_from_lane_byte = {16'b0, b, 8'b0};
2'b10: dat_from_lane_byte = {8'b0, b, 16'b0};
default: dat_from_lane_byte = {b, 24'b0};
endcase
endfunction
function [7:0] byte_from_lane(input [1:0] lane, input [31:0] w);
case (lane)
2'b00: byte_from_lane = w[7:0];
2'b01: byte_from_lane = w[15:8];
2'b10: byte_from_lane = w[23:16];
default: byte_from_lane = w[31:24];
endcase
endfunction
// Build response word
function [JTAG_DR_W-1:0] pack_resp(
input [7:0] resp_seq,
input [7:0] status,
input [7:0] cmd_seq,
input [31:0] data,
input [7:0] flags,
input [7:0] last_op
);
pack_resp = {resp_seq, status, cmd_seq, data, flags, last_op};
endfunction
// STATUS bits (snapshot)
wire [7:0] status_snapshot = {
2'b00, // [7:6]
1'b1, // [5] resp_valid
last_we_r, // [4] last_we
cmd_reset_level_r, // [3] reset_level
b_resp_busy, // [2] resp_busy (system domain)
req_busy_tck_sync, // [1] req_busy (synced from TCK just for reporting)
wb_busy // [0] wb_busy
};
// FLAGS bits (sticky)
wire [7:0] flags_sticky = {
4'b0000, // [7:4] reserved
1'b0, // [3] reserved
flag_wb_busy_at_req, // [2]
flag_illegal, // [1]
flag_cmd_overflow // [0]
};
// Queue a command (or set overflow sticky if queue full)
task automatic enqueue_cmd(
input [7:0] op,
input [31:0] addr,
input [31:0] dat,
input [7:0] seq
);
begin
if (!q_valid) begin
q_valid <= 1'b1;
q_opcode <= op;
q_addr <= addr;
q_data <= dat;
q_seq <= seq;
end else begin
// Already have one queued; mark overflow and drop this command
flag_cmd_overflow <= 1'b1;
end
end
endtask
// Start executing a command (for non-WB ops may immediately create a response)
task automatic start_active_cmd(
input [7:0] cmd_opcode,
input [31:0] cmd_addr,
input [31:0] cmd_data,
input [7:0] cmd_seq
);
reg [1:0] cmd_addr_lane;
begin
cmd_addr_lane = byte_aligned ? 2'b00 : cmd_addr[1:0];
last_opcode_r <= cmd_opcode;
last_we_r <= (cmd_opcode == OP_WRITE8) || (cmd_opcode == OP_WRITE32);
// If we're already mid-flight or holding a response, note it (diagnostic)
if (wb_busy || resp_pending)
flag_wb_busy_at_req <= 1'b1;
case (cmd_opcode)
OP_NOP: begin
// immediate response
resp_pending_word <= pack_resp(resp_seq_r, status_snapshot, cmd_seq, 8'h00, flags_sticky, cmd_opcode);
resp_pending <= 1'b1;
end
OP_PING: begin
resp_pending_word <= pack_resp(resp_seq_r, status_snapshot, cmd_seq, 8'hA5, flags_sticky, cmd_opcode);
resp_pending <= 1'b1;
end
OP_CLEAR_FLAGS: begin
flag_cmd_overflow <= 1'b0;
flag_illegal <= 1'b0;
flag_wb_busy_at_req <= 1'b0;
resp_pending_word <= pack_resp(resp_seq_r, status_snapshot, cmd_seq, 8'h00, 8'h00, cmd_opcode);
resp_pending <= 1'b1;
end
OP_RESET_ON: begin
cmd_reset_level_r <= 1'b1;
resp_pending_word <= pack_resp(resp_seq_r, status_snapshot, cmd_seq, 8'h00, flags_sticky, cmd_opcode);
resp_pending <= 1'b1;
end
OP_RESET_OFF: begin
cmd_reset_level_r <= 1'b0;
resp_pending_word <= pack_resp(resp_seq_r, status_snapshot, cmd_seq, 8'h00, flags_sticky, cmd_opcode);
resp_pending <= 1'b1;
end
OP_WRITE8: begin
// launch WB write (byte)
wb_busy <= 1'b1;
wb_we_r <= 1'b1;
wb_adr_r <= cmd_addr;
wb_sel_r <= sel_from_lane(cmd_addr_lane);
wb_dat_r <= dat_from_lane_byte(cmd_addr_lane, cmd_data[7:0]);
end
OP_READ8: begin
// launch WB read (byte select)
wb_busy <= 1'b1;
wb_we_r <= 1'b0;
wb_adr_r <= cmd_addr;
wb_sel_r <= sel_from_lane(cmd_addr_lane);
wb_dat_r <= 32'b0;
end
OP_WRITE32: begin
// launch WB write (full word)
wb_busy <= 1'b1;
wb_we_r <= 1'b1;
wb_adr_r <= cmd_addr;
wb_sel_r <= 4'b1111;
wb_dat_r <= cmd_data;
end
OP_READ32: begin
// launch WB read (full word)
wb_busy <= 1'b1;
wb_we_r <= 1'b0;
wb_adr_r <= cmd_addr;
wb_sel_r <= 4'b1111;
wb_dat_r <= 32'b0;
end
default: begin
flag_illegal <= 1'b1;
resp_pending_word <= pack_resp(resp_seq_r, status_snapshot, cmd_seq, 8'h00, flags_sticky, cmd_opcode);
resp_pending <= 1'b1;
end
endcase
end
endtask
// System main
always @(posedge i_clk) begin always @(posedge i_clk) begin
if (i_rst) 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_busy <= 1'b0;
wb_adr_r <= 32'b0; wb_adr_r <= 32'b0;
wb_dat_r <= 32'b0; wb_dat_r <= 32'b0;
wb_sel_r <= 4'b0000; wb_sel_r <= 4'b0000;
wb_we_r <= 1'b0; wb_we_r <= 1'b0;
cmd_reset_pulse_r <= 1'b0;
resp_addr_r <= 32'b0; cmd_reset_level_r<= 1'b0;
resp_data_r <= 8'b0;
req_busy_sync1 <= 1'b0;
req_busy_sync2 <= 1'b0;
cmd_seq_r <= 8'd0;
resp_seq_r <= 8'd0;
flag_cmd_overflow<= 1'b0;
flag_illegal <= 1'b0;
flag_wb_busy_at_req <= 1'b0;
last_we_r <= 1'b0;
last_opcode_r <= 8'h00;
act_valid <= 1'b0;
act_opcode <= 8'h00;
act_addr <= 32'h0;
act_data <= 32'h0000_0000;
act_seq <= 8'h00;
q_valid <= 1'b0;
q_opcode <= 8'h00;
q_addr <= 32'h0;
q_data <= 32'h0000_0000;
q_seq <= 8'h00;
resp_pending <= 1'b0;
resp_pending_word<= {JTAG_DR_W{1'b0}};
b_resp_pulse <= 1'b0;
b_resp_data <= {JTAG_DR_W{1'b0}};
end else begin end else begin
s_req_sync_1 <= j_req_tgl; b_resp_pulse <= 1'b0;
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 // Sync req-busy level (reporting only)
wb_busy <= 1'b1; req_busy_sync1 <= a_req_busy;
wb_we_r <= cmd_we; req_busy_sync2 <= req_busy_sync1;
wb_adr_r <= cmd_addr;
case (req_lane) // -----------------------------------------------------------------------
2'b00: begin wb_sel_r <= 4'b0001; wb_dat_r <= {24'b0, cmd_wdata}; end // Accept incoming command from CDC (always delivered; we buffer internally)
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 if (b_req_pulse) begin
default: begin wb_sel_r <= 4'b1000; wb_dat_r <= {cmd_wdata, 24'b0}; end // assign a sequence number to each received command
endcase cmd_seq_r <= cmd_seq_r + 8'd1;
cmd_reset_pulse_r <= cmd_reset; // If we can start immediately (no active, no wb, no pending response), do so.
if (!act_valid && !wb_busy && !resp_pending) begin
act_valid <= 1'b1;
act_opcode <= b_req_data[71:64];
act_addr <= b_req_data[63:32];
act_data <= b_req_data[31:0];
act_seq <= cmd_seq_r;
// Start it right away
start_active_cmd(b_req_data[71:64], b_req_data[63:32], b_req_data[31:0], cmd_seq_r);
end else begin
// Otherwise enqueue one-deep
enqueue_cmd(b_req_data[71:64], b_req_data[63:32], b_req_data[31:0], cmd_seq_r);
end
end end
// -----------------------------------------------------------------------
// Wishbone completion -> create response (but don't drop; buffer pending)
// -----------------------------------------------------------------------
if (wb_busy && i_wb_ack) begin if (wb_busy && i_wb_ack) begin
wb_busy <= 1'b0; wb_busy <= 1'b0;
wb_we_r <= 1'b0; wb_we_r <= 1'b0;
resp_addr_r <= wb_adr_r;
case (resp_lane) // Determine response data
2'b00: resp_data_r <= i_wb_rdt[7:0]; case (act_opcode)
2'b01: resp_data_r <= i_wb_rdt[15:8]; OP_READ8: begin
2'b10: resp_data_r <= i_wb_rdt[23:16]; resp_pending_word <= pack_resp(
default: resp_data_r <= i_wb_rdt[31:24]; resp_seq_r,
status_snapshot,
act_seq,
{24'b0, byte_from_lane(addr_lane, i_wb_rdt)},
flags_sticky,
act_opcode
);
end
OP_READ32: begin
resp_pending_word <= pack_resp(
resp_seq_r,
status_snapshot,
act_seq,
i_wb_rdt,
flags_sticky,
act_opcode
);
end
default: begin
// WRITE8/WRITE32: echo written data
resp_pending_word <= pack_resp(
resp_seq_r,
status_snapshot,
act_seq,
act_data,
flags_sticky,
act_opcode
);
end
endcase endcase
resp_pending <= 1'b1;
s_ack_tgl <= s_req_sync_2;
end end
// -----------------------------------------------------------------------
// If we have a pending response and response mailbox is free, send it
// -----------------------------------------------------------------------
if (resp_pending && !b_resp_busy) begin
b_resp_data <= resp_pending_word;
b_resp_pulse <= 1'b1;
resp_pending <= 1'b0;
resp_seq_r <= resp_seq_r + 8'd1;
// Mark active command complete
act_valid <= 1'b0;
// If there is a queued command, promote and start it
if (q_valid) begin
act_valid <= 1'b1;
act_opcode <= q_opcode;
act_addr <= q_addr;
act_data <= q_data;
act_seq <= q_seq;
q_valid <= 1'b0;
start_active_cmd(q_opcode, q_addr, q_data, q_seq);
end end
end end
assign jtag_data_in = {2'b00, resp_addr_r, resp_data_r}; // -----------------------------------------------------------------------
// If no active command but there is a queued one (and we're not busy), start it
// -----------------------------------------------------------------------
if (!act_valid && q_valid && !wb_busy && !resp_pending) begin
act_valid <= 1'b1;
act_opcode <= q_opcode;
act_addr <= q_addr;
act_data <= q_data;
act_seq <= q_seq;
q_valid <= 1'b0;
start_active_cmd(q_opcode, q_addr, q_data, q_seq);
end
end
end
endmodule endmodule

101
rtl/wb/wb_arbiter.v Normal file
View File

@@ -0,0 +1,101 @@
/* wb_arbiter. 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 arbiter, burst-compatible
Simple round-robin arbiter for multiple Wishbone masters
*/
`include "../util/clog2.vh"
module wb_arbiter
#(parameter dw = 32,
parameter aw = 32,
parameter num_hosts = 0,
parameter num_masters = num_hosts)
(
input wire wb_clk_i,
input wire wb_rst_i,
// Wishbone Master Interface
input wire [num_masters*aw-1:0] wbm_adr_i,
input wire [num_masters*dw-1:0] wbm_dat_i,
input wire [num_masters*4-1:0] wbm_sel_i,
input wire [num_masters-1:0] wbm_we_i,
input wire [num_masters-1:0] wbm_cyc_i,
input wire [num_masters-1:0] wbm_stb_i,
input wire [num_masters*3-1:0] wbm_cti_i,
input wire [num_masters*2-1:0] wbm_bte_i,
output wire [num_masters*dw-1:0] wbm_dat_o,
output wire [num_masters-1:0] wbm_ack_o,
output wire [num_masters-1:0] wbm_err_o,
output wire [num_masters-1:0] wbm_rty_o,
// Wishbone Slave interface
output wire [aw-1:0] wbs_adr_o,
output wire [dw-1:0] wbs_dat_o,
output wire [3:0] wbs_sel_o,
output wire wbs_we_o,
output wire wbs_cyc_o,
output wire wbs_stb_o,
output wire [2:0] wbs_cti_o,
output wire [1:0] wbs_bte_o,
input wire [dw-1:0] wbs_dat_i,
input wire wbs_ack_i,
input wire wbs_err_i,
input wire wbs_rty_i);
///////////////////////////////////////////////////////////////////////////////
// Parameters
///////////////////////////////////////////////////////////////////////////////
//Use parameter instead of localparam to work around a bug in Xilinx ISE
parameter master_sel_bits = num_masters > 1 ? `CLOG2(num_masters) : 1;
wire [num_masters-1:0] grant;
wire [master_sel_bits-1:0] master_sel;
wire active;
arbiter
#(.NUM_PORTS (num_masters))
arbiter0
(.clk (wb_clk_i),
.rst (wb_rst_i),
.request (wbm_cyc_i),
.grant (grant),
.select (master_sel),
.active (active));
/* verilator lint_off WIDTH */
//Mux active master
assign wbs_adr_o = wbm_adr_i[master_sel*aw+:aw];
assign wbs_dat_o = wbm_dat_i[master_sel*dw+:dw];
assign wbs_sel_o = wbm_sel_i[master_sel*4+:4];
assign wbs_we_o = wbm_we_i [master_sel];
assign wbs_cyc_o = wbm_cyc_i[master_sel] & active;
assign wbs_stb_o = wbm_stb_i[master_sel];
assign wbs_cti_o = wbm_cti_i[master_sel*3+:3];
assign wbs_bte_o = wbm_bte_i[master_sel*2+:2];
assign wbm_dat_o = {num_masters{wbs_dat_i}};
assign wbm_ack_o = ((wbs_ack_i & active) << master_sel);
assign wbm_err_o = ((wbs_err_i & active) << master_sel);
assign wbm_rty_o = ((wbs_rty_i & active) << master_sel);
/* verilator lint_on WIDTH */
endmodule // wb_arbiter

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Convert a simple .hex image to a plain .mif-style binary file. """Convert a simple .hex image to a plain .mif-style binary file.
Default output format matches build/mem_8kx8b.mif in this repo: Default output format is 32-bit words:
- one binary word per line - one binary word per line
- no header - no header
""" """
@@ -57,8 +57,8 @@ def main() -> None:
parser.add_argument( parser.add_argument(
"--word-bytes", "--word-bytes",
type=int, type=int,
default=1, default=4,
help="Bytes per output word (default: 1)", help="Bytes per output word (default: 4)",
) )
parser.add_argument( parser.add_argument(
"--little-endian", "--little-endian",

View File

@@ -1,10 +0,0 @@
TRST ABSENT;
ENDIR IDLE;
ENDDR IDLE;
STATE RESET;
STATE IDLE;
SIR 6 TDI (02);
SDR 42 TDI (3A987654321);
STATE IDLE;

124
sim/tb/tb_cdc_strobe_data.v Normal file
View File

@@ -0,0 +1,124 @@
`timescale 1ns/1ps
module tb_cdc_strobe_data;
localparam integer WIDTH = 16;
localparam integer NUM_TX = 5;
// Source domain
reg s_clk;
reg s_rst;
reg s_pulse;
reg [WIDTH-1:0] s_data;
wire s_busy;
wire s_accepted;
// Destination domain
reg d_clk;
reg d_rst;
wire d_pulse;
wire [WIDTH-1:0] d_data;
reg [WIDTH-1:0] exp_data [0:NUM_TX-1];
integer rx_idx;
integer tx_idx;
cdc_strobe_data #(
.WIDTH(WIDTH),
.STABLE_SAMPLES(2)
) dut (
.s_clk(s_clk),
.s_rst(s_rst),
.s_pulse(s_pulse),
.s_data(s_data),
.s_busy(s_busy),
.s_accepted(s_accepted),
.d_clk(d_clk),
.d_rst(d_rst),
.d_pulse(d_pulse),
.d_data(d_data)
);
initial s_clk = 1'b0;
always #625 s_clk = ~s_clk; // 800 kHz
initial d_clk = 1'b0;
always #33.333 d_clk = ~d_clk; // 15 MHz (asynchronous to s_clk)
task send_word;
input [WIDTH-1:0] value;
begin
@(posedge s_clk);
while (s_busy)
@(posedge s_clk);
s_data <= value;
s_pulse <= 1'b1;
@(posedge s_clk);
if (!s_accepted) begin
$display("[%0t] ERROR: expected s_accepted for value 0x%0h", $time, value);
$fatal(1);
end
s_pulse <= 1'b0;
s_data <= {WIDTH{1'b0}};
end
endtask
always @(posedge d_clk) begin
if (!d_rst && d_pulse) begin
if (rx_idx >= NUM_TX) begin
$display("[%0t] ERROR: unexpected extra d_pulse with data=0x%0h", $time, d_data);
$fatal(1);
end
if (d_data !== exp_data[rx_idx]) begin
$display("[%0t] ERROR: rx[%0d] expected 0x%0h, got 0x%0h",
$time, rx_idx, exp_data[rx_idx], d_data);
$fatal(1);
end
$display("[%0t] INFO: rx[%0d] = 0x%0h", $time, rx_idx, d_data);
rx_idx <= rx_idx + 1;
end
end
initial begin
$dumpfile("out.vcd");
$dumpvars(0, tb_cdc_strobe_data);
s_rst = 1'b1;
d_rst = 1'b1;
s_pulse = 1'b0;
s_data = {WIDTH{1'b0}};
rx_idx = 0;
tx_idx = 0;
exp_data[0] = 16'h1234;
exp_data[1] = 16'h00A5;
exp_data[2] = 16'hBEEF;
exp_data[3] = 16'h5AA5;
exp_data[4] = 16'hCAFE;
repeat (4) @(posedge s_clk);
s_rst = 1'b0;
repeat (3) @(posedge d_clk);
d_rst = 1'b0;
for (tx_idx = 0; tx_idx < NUM_TX; tx_idx = tx_idx + 1)
send_word(exp_data[tx_idx]);
wait (rx_idx == NUM_TX);
$display("[%0t] PASS: received %0d/%0d transfers correctly", $time, rx_idx, NUM_TX);
#20;
$finish;
end
initial begin
#100000000;
$display("[%0t] ERROR: timeout waiting for transfers", $time);
$fatal(1);
end
endmodule

334
sim/tb/tb_jtag_wb_bridge.v Normal file
View File

@@ -0,0 +1,334 @@
`timescale 1ns/1ps
module tb_jtag_wb_bridge;
localparam [7:0] OP_NOP = 8'h00;
localparam [7:0] OP_RESET_ON = 8'h10;
localparam [7:0] OP_RESET_OFF = 8'h11;
localparam [7:0] OP_WRITE8 = 8'h20;
localparam [7:0] OP_READ8 = 8'h21;
localparam [7:0] OP_PING = 8'h30;
reg i_clk;
reg i_rst;
wire [31:0] o_wb_adr;
wire [31:0] o_wb_dat;
wire [3:0] o_wb_sel;
wire o_wb_we;
wire o_wb_cyc;
wire o_wb_stb;
reg [31:0] i_wb_rdt;
reg i_wb_ack;
wire o_cmd_reset;
reg [7:0] mem [0:1023];
reg wb_req_pending;
reg [31:0] wb_addr_latched;
reg [31:0] wb_dat_latched;
reg [3:0] wb_sel_latched;
reg wb_we_latched;
integer wb_base;
integer idx;
integer req_accept_count;
reg saw_write_lane3;
reg saw_read_lane3;
reg saw_cmd_reset_high;
reg [47:0] flush_rx;
jtag_wb_bridge #(
.chain(1),
.byte_aligned(0)
) dut (
.i_clk(i_clk),
.i_rst(i_rst),
.o_wb_adr(o_wb_adr),
.o_wb_dat(o_wb_dat),
.o_wb_sel(o_wb_sel),
.o_wb_we(o_wb_we),
.o_wb_cyc(o_wb_cyc),
.o_wb_stb(o_wb_stb),
.i_wb_rdt(i_wb_rdt),
.i_wb_ack(i_wb_ack),
.o_cmd_reset(o_cmd_reset)
);
function [47:0] make_cmd;
input [7:0] op;
input [31:0] addr;
input [7:0] data;
begin
make_cmd = {op, addr, data};
end
endfunction
task expect_resp;
input [47:0] resp;
input [7:0] exp_data;
input [7:0] exp_last_op;
begin
if (resp[23:16] !== exp_data) begin
$display("[%0t] ERROR: data exp=0x%0h got=0x%0h", $time, exp_data, resp[23:16]);
$fatal(1);
end
if (resp[7:0] !== exp_last_op) begin
$display("[%0t] ERROR: last_op exp=0x%0h got=0x%0h", $time, exp_last_op, resp[7:0]);
$fatal(1);
end
if (resp[37] !== 1'b1) begin
$display("[%0t] ERROR: status.resp_valid bit not set in response 0x%012h", $time, resp);
$fatal(1);
end
end
endtask
task send_cmd;
input [47:0] cmd_word;
integer tries;
integer start_accepts;
reg [47:0] dummy_rx;
begin
start_accepts = req_accept_count;
for (tries = 0; tries < 8; tries = tries + 1) begin
do_xfer(cmd_word, dummy_rx);
if (req_accept_count > start_accepts)
tries = 8;
end
if (req_accept_count <= start_accepts) begin
$display("[%0t] ERROR: command 0x%0h was never accepted", $time, cmd_word[47:40]);
$fatal(1);
end
end
endtask
task expect_eventual_response;
input [7:0] exp_data;
input [7:0] exp_last_op;
integer tries;
reg [47:0] rx_word;
reg found;
begin
found = 1'b0;
for (tries = 0; tries < 8; tries = tries + 1) begin
do_xfer(make_cmd(OP_NOP, 32'h0000_0000, 8'h00), rx_word);
if (rx_word[7:0] == exp_last_op) begin
expect_resp(rx_word, exp_data, exp_last_op);
found = 1'b1;
tries = 8;
end
end
if (!found) begin
$display("[%0t] ERROR: did not observe response for last_op=0x%0h", $time, exp_last_op);
$fatal(1);
end
end
endtask
task do_xfer;
input [47:0] tx_word;
output [47:0] rx_word;
begin
dut.u_jtag.transceive48(tx_word, rx_word);
repeat (400) @(posedge i_clk);
end
endtask
initial i_clk = 1'b0;
always #10 i_clk = ~i_clk; // 50 MHz
always @(posedge i_clk) begin
if (i_rst) begin
i_wb_ack <= 1'b0;
i_wb_rdt <= 32'h0;
wb_req_pending <= 1'b0;
wb_addr_latched <= 32'h0;
wb_dat_latched <= 32'h0;
wb_sel_latched <= 4'h0;
wb_we_latched <= 1'b0;
end else begin
if (o_cmd_reset)
saw_cmd_reset_high <= 1'b1;
i_wb_ack <= 1'b0;
if (wb_req_pending) begin
wb_base = {wb_addr_latched[31:2], 2'b00};
if (wb_we_latched) begin
if (wb_sel_latched[0]) mem[wb_base + 0] <= wb_dat_latched[7:0];
if (wb_sel_latched[1]) mem[wb_base + 1] <= wb_dat_latched[15:8];
if (wb_sel_latched[2]) mem[wb_base + 2] <= wb_dat_latched[23:16];
if (wb_sel_latched[3]) mem[wb_base + 3] <= wb_dat_latched[31:24];
end
i_wb_rdt <= {mem[wb_base + 3], mem[wb_base + 2], mem[wb_base + 1], mem[wb_base + 0]};
i_wb_ack <= 1'b1;
wb_req_pending <= 1'b0;
end else if (o_wb_cyc && o_wb_stb) begin
wb_addr_latched <= o_wb_adr;
wb_dat_latched <= o_wb_dat;
wb_sel_latched <= o_wb_sel;
wb_we_latched <= o_wb_we;
wb_req_pending <= 1'b1;
if (o_wb_we && (o_wb_adr == 32'h0000_0013) && (o_wb_sel == 4'b1000) && (o_wb_dat == 32'h5A00_0000))
saw_write_lane3 <= 1'b1;
if (!o_wb_we && (o_wb_adr == 32'h0000_0013) && (o_wb_sel == 4'b1000))
saw_read_lane3 <= 1'b1;
end
end
end
always @(posedge dut.jtag_tck) begin
if (i_rst)
req_accept_count <= 0;
else if (dut.a_req_accepted)
req_accept_count <= req_accept_count + 1;
end
initial begin
$dumpfile("out.vcd");
$dumpvars(0, tb_jtag_wb_bridge);
i_rst = 1'b1;
i_wb_rdt = 32'h0;
i_wb_ack = 1'b0;
wb_req_pending = 1'b0;
wb_addr_latched = 32'h0;
wb_dat_latched = 32'h0;
wb_sel_latched = 4'h0;
wb_we_latched = 1'b0;
saw_write_lane3 = 1'b0;
saw_read_lane3 = 1'b0;
saw_cmd_reset_high = 1'b0;
req_accept_count = 0;
for (idx = 0; idx < 1024; idx = idx + 1)
mem[idx] = idx[7:0];
repeat (10) @(posedge i_clk);
i_rst = 1'b0;
repeat (10) @(posedge i_clk);
send_cmd(make_cmd(OP_PING, 32'h0000_0000, 8'h00));
expect_eventual_response(8'hA5, OP_PING);
send_cmd(make_cmd(OP_WRITE8, 32'h0000_0013, 8'h5A));
send_cmd(make_cmd(OP_READ8, 32'h0000_0013, 8'h00));
send_cmd(make_cmd(OP_RESET_ON, 32'h0000_0000, 8'h00));
send_cmd(make_cmd(OP_RESET_OFF, 32'h0000_0000, 8'h00));
repeat (6)
do_xfer(make_cmd(OP_NOP, 32'h0000_0000, 8'h00), flush_rx);
if (!saw_write_lane3) begin
$display("[%0t] ERROR: expected WRITE8 lane-3 WB access not observed", $time);
$fatal(1);
end
if (!saw_read_lane3) begin
$display("[%0t] ERROR: expected READ8 lane-3 WB access not observed", $time);
$fatal(1);
end
if (!saw_cmd_reset_high) begin
$display("[%0t] ERROR: expected o_cmd_reset to go high at least once", $time);
$fatal(1);
end
repeat (50) @(posedge i_clk);
if (o_cmd_reset !== 1'b0) begin
$display("[%0t] ERROR: expected o_cmd_reset low after RESET_OFF, got %0b", $time, o_cmd_reset);
$fatal(1);
end
$display("[%0t] PASS: jtag_wb_bridge basic command/response path verified", $time);
#100;
$finish;
end
initial begin
#5_000_000;
$display("[%0t] ERROR: timeout", $time);
$fatal(1);
end
endmodule
module jtag_if #(
parameter integer chain = 1
)(
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 k;
// Quiet unused parameter warning
wire _unused_chain;
assign _unused_chain = chain[0];
task pulse_tck;
begin
#40 o_tck = 1'b1;
#40 o_tck = 1'b0;
end
endtask
task transceive48;
input [47:0] tx_word;
output [47:0] rx_word;
begin
rx_word = 48'h0;
// Let CDC response path advance in the TCK domain before CAPTURE.
for (k = 0; k < 32; k = k + 1)
pulse_tck;
// CAPTURE-DR
o_capture = 1'b1;
pulse_tck;
o_capture = 1'b0;
// SHIFT-DR (read previous response while shifting next command in)
o_shift = 1'b1;
for (k = 0; k < 48; k = k + 1) begin
o_tdi = tx_word[k];
rx_word[k] = i_tdo;
o_drck = 1'b1;
pulse_tck;
o_drck = 1'b0;
end
o_shift = 1'b0;
// UPDATE-DR (request strobe sampled by TCK)
o_update = 1'b1;
pulse_tck;
o_update = 1'b0;
// Provide a few extra clocks after UPDATE.
for (k = 0; k < 8; k = k + 1)
pulse_tck;
end
endtask
initial begin
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'b1;
end
endmodule

View File

@@ -1,106 +0,0 @@
`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

View File

@@ -11,7 +11,7 @@ module tb_top_generic();
// 100 MHz board input clock // 100 MHz board input clock
initial aclk = 1'b0; initial aclk = 1'b0;
always #5 aclk = ~aclk; always #33.33 aclk = ~aclk;
// Hold reset low, then release // Hold reset low, then release
initial begin initial begin
@@ -39,7 +39,7 @@ module tb_top_generic();
$dumpvars(0, tb_top_generic); $dumpvars(0, tb_top_generic);
// Let firmware run for a while. // Let firmware run for a while.
#5_000_000; #2_000_000;
$finish; $finish;
end end
endmodule endmodule

View File

@@ -1,56 +0,0 @@
TOOLCHAIN_PREFIX ?= riscv64-elf-
CC := $(TOOLCHAIN_PREFIX)gcc
OBJCOPY := $(TOOLCHAIN_PREFIX)objcopy
OBJDUMP := $(TOOLCHAIN_PREFIX)objdump
SIZE := $(TOOLCHAIN_PREFIX)size
TARGET := blinky
SRCS_C := blinky.c
SRCS_S := start.s
OBJS := $(SRCS_C:.c=.o) $(SRCS_S:.s=.o)
ARCH_FLAGS := -march=rv32i_zicsr -mabi=ilp32
CFLAGS := $(ARCH_FLAGS) -Os -ffreestanding -fno-builtin -Wall -Wextra
ASFLAGS := $(ARCH_FLAGS)
LDFLAGS := $(ARCH_FLAGS) -nostdlib -nostartfiles -Wl,-Bstatic,-Tlink.ld,--gc-sections,-Map,$(TARGET).map
HEX_TO_COE := ../../scripts/hex_to_coe.py
HEX_TO_MIF := ../../scripts/hex_to_mif.py
.PHONY: all clean disasm size
all: $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).coe $(TARGET).mif $(TARGET).elf.asm
$(TARGET).elf: $(OBJS) link.ld
$(CC) $(LDFLAGS) -o $@ $(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.s
$(CC) $(ASFLAGS) -c -o $@ $<
$(TARGET).bin: $(TARGET).elf
$(OBJCOPY) -O binary $< $@
$(TARGET).hex: $(TARGET).bin
hexdump -v -e '1/1 "%02x\n"' $< > $@
$(TARGET).coe: $(TARGET).hex
$(HEX_TO_COE) $< $@
$(TARGET).mif: $(TARGET).hex
$(HEX_TO_MIF) $< $@
$(TARGET).elf.asm: $(TARGET).elf
$(OBJDUMP) -d -S $< > $@
disasm: $(TARGET).elf.asm
size: $(TARGET).elf
$(SIZE) $<
clean:
rm -f $(TARGET).elf $(TARGET).bin $(TARGET).hex $(TARGET).coe $(TARGET).mif \
$(TARGET).elf.asm $(TARGET).map $(OBJS)

View File

@@ -1,27 +0,0 @@
#include <stdint.h>
#define GPIO_BASE 0x40000000u
#define VOUT_BASE 0x40000004u
static volatile uint32_t * const gpio = (volatile uint32_t *)GPIO_BASE;
static volatile uint32_t * const vout = (volatile uint32_t *)VOUT_BASE;
static void delay(volatile uint32_t ticks){
while (ticks--) {
__asm__ volatile ("nop");
}
}
int main(void)
{
uint32_t v = 0;
for (;;) {
for(int i=0; i<1000; i++){
*vout = v;
v++;
delay(5u);
}
*gpio ^= 0xffffffff;
}
}

View File

@@ -1,33 +0,0 @@
OUTPUT_ARCH("riscv")
ENTRY(_start)
MEMORY
{
RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064
}
SECTIONS
{
.text :
{
KEEP(*(.text.init))
*(.text .text.*)
*(.rodata .rodata.*)
} > RAM
.data :
{
*(.data .data.*)
} > RAM
.bss (NOLOAD) :
{
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
} > RAM
. = ALIGN(4);
__stack_top = ORIGIN(RAM) + LENGTH(RAM);
}

View File

@@ -1,23 +0,0 @@
.section .text.init
.globl _start
.type _start, @function
_start:
la sp, __stack_top
# Zero .bss
la t0, __bss_start
la t1, __bss_end
1:
bgeu t0, t1, 2f
sw zero, 0(t0)
addi t0, t0, 4
j 1b
2:
call main
3:
j 3b
.size _start, .-_start

View File

@@ -35,7 +35,7 @@ $(TARGET).bin: $(TARGET).elf
$(OBJCOPY) -O binary $< $@ $(OBJCOPY) -O binary $< $@
$(TARGET).hex: $(TARGET).bin $(TARGET).hex: $(TARGET).bin
hexdump -v -e '1/1 "%02x\n"' $< > $@ hexdump -v -e '1/4 "%08x\n"' $< > $@
$(TARGET).coe: $(TARGET).hex $(TARGET).coe: $(TARGET).hex
$(HEX_TO_COE) $< $@ $(HEX_TO_COE) $< $@

View File

@@ -24,10 +24,12 @@ SECTIONS
{ {
__bss_start = .; __bss_start = .;
*(.bss .bss.*) *(.bss .bss.*)
*(.sbss .sbss.*)
*(.scommon)
*(COMMON) *(COMMON)
__bss_end = .; __bss_end = .;
} > RAM } > RAM
. = ALIGN(4); . = ALIGN(4);
__stack_top = ORIGIN(RAM) + LENGTH(RAM) - 256; __stack_top = ORIGIN(RAM) + LENGTH(RAM);
} }

View File

@@ -1,7 +1,6 @@
.section .text.init .section .text.init
.globl _start .globl _start
.type _start, @function .type _start, @function
_start: _start:
la sp, __stack_top la sp, __stack_top
@@ -21,3 +20,80 @@ _start:
j 3b j 3b
.size _start, .-_start .size _start, .-_start
.section .text
.globl trap_entry
.type trap_entry, @function
trap_entry:
# Save full integer context (except x0/x2) because an interrupt can
# preempt code with live values in any register, not just caller-saved.
addi sp, sp, -128
sw ra, 124(sp)
sw gp, 120(sp)
sw tp, 116(sp)
sw t0, 112(sp)
sw t1, 108(sp)
sw t2, 104(sp)
sw s0, 100(sp)
sw s1, 96(sp)
sw a0, 92(sp)
sw a1, 88(sp)
sw a2, 84(sp)
sw a3, 80(sp)
sw a4, 76(sp)
sw a5, 72(sp)
sw a6, 68(sp)
sw a7, 64(sp)
sw s2, 60(sp)
sw s3, 56(sp)
sw s4, 52(sp)
sw s5, 48(sp)
sw s6, 44(sp)
sw s7, 40(sp)
sw s8, 36(sp)
sw s9, 32(sp)
sw s10, 28(sp)
sw s11, 24(sp)
sw t3, 20(sp)
sw t4, 16(sp)
sw t5, 12(sp)
sw t6, 8(sp)
csrr t0, mcause
li t1, 0x80000007 # machine timer interrupt (RV32)
bne t0, t1, 1f
call timer_isr # C function that ACKs/clears the timer so i_timer_irq goes low
1:
lw t6, 8(sp)
lw t5, 12(sp)
lw t4, 16(sp)
lw t3, 20(sp)
lw s11, 24(sp)
lw s10, 28(sp)
lw s9, 32(sp)
lw s8, 36(sp)
lw s7, 40(sp)
lw s6, 44(sp)
lw s5, 48(sp)
lw s4, 52(sp)
lw s3, 56(sp)
lw s2, 60(sp)
lw a7, 64(sp)
lw a6, 68(sp)
lw a5, 72(sp)
lw a4, 76(sp)
lw a3, 80(sp)
lw a2, 84(sp)
lw a1, 88(sp)
lw a0, 92(sp)
lw s1, 96(sp)
lw s0, 100(sp)
lw t2, 104(sp)
lw t1, 108(sp)
lw t0, 112(sp)
lw tp, 116(sp)
lw gp, 120(sp)
lw ra, 124(sp)
addi sp, sp, 128
mret

View File

@@ -1,14 +1,45 @@
#include <stdint.h> #include <stdint.h>
#define GPIO_BASE 0x40000000u #define GPIO_BASE 0x40000000u
static volatile uint32_t * const R_FREQ = (volatile uint32_t *)(GPIO_BASE+0);
static volatile uint32_t * const LEDS = (volatile uint32_t *)(GPIO_BASE+4);
static volatile uint32_t * const LEDGR = (volatile uint32_t *)(GPIO_BASE+8);
static volatile uint32_t * const R_FREQ = (volatile uint32_t *)GPIO_BASE; #define TIMER_BASE 0x40010000u
static volatile uint32_t * const TIMER = (volatile uint32_t *)(TIMER_BASE+0);
#define MSTATUS_MIE (1u << 3)
#define MIE_MTIE (1u << 7)
extern void trap_entry();
static inline void irq_init() {
/* mtvec first */
asm volatile ("csrw mtvec, %0" :: "r"(trap_entry));
/* enable machine timer interrupt */
asm volatile ("csrs mie, %0" :: "r"(MIE_MTIE));
/* global enable last */
asm volatile ("csrs mstatus, %0" :: "r"(MSTATUS_MIE));
}
void timer_isr(){
static int set = 0;
*TIMER = 1840000*2;
*LEDGR = ~(*LEDGR);
}
void main(){ void main(){
irq_init();
*LEDGR = 3;
*TIMER = 1840000*2;
for(;;){ for(;;){
for(int i=1000; i<10000; i++){ for(int i=1000; i<10000; i++){
*R_FREQ = i; *R_FREQ = i;
// for(int j=0; j<100; j++) asm volatile("nop"); for(int j=0; j<80; j++) asm volatile("nop");
} }
} }
} }

View File

@@ -12,21 +12,41 @@ ArgParser::ArgParser(std::string program_name)
void ArgParser::addString(const std::string &name, void ArgParser::addString(const std::string &name,
const std::string &default_value, const std::string &default_value,
const std::string &help, const std::string &help,
bool required) { bool required,
const std::string &short_name) {
order_.push_back(name); order_.push_back(name);
meta_[name] = {OptionType::kString, help, required}; meta_[name] = {OptionType::kString, help, required, short_name};
string_values_[name] = default_value; string_values_[name] = default_value;
provided_[name] = false; provided_[name] = false;
if (!short_name.empty()) {
short_to_long_[short_name] = name;
}
} }
void ArgParser::addInt(const std::string &name, void ArgParser::addInt(const std::string &name,
int default_value, int default_value,
const std::string &help, const std::string &help,
bool required) { bool required,
const std::string &short_name) {
order_.push_back(name); order_.push_back(name);
meta_[name] = {OptionType::kInt, help, required}; meta_[name] = {OptionType::kInt, help, required, short_name};
int_values_[name] = default_value; int_values_[name] = default_value;
provided_[name] = false; provided_[name] = false;
if (!short_name.empty()) {
short_to_long_[short_name] = name;
}
}
void ArgParser::addFlag(const std::string &name,
const std::string &help,
const std::string &short_name) {
order_.push_back(name);
meta_[name] = {OptionType::kFlag, help, false, short_name};
flag_values_[name] = false;
provided_[name] = false;
if (!short_name.empty()) {
short_to_long_[short_name] = name;
}
} }
bool ArgParser::parse(int argc, char **argv, std::string *error) { bool ArgParser::parse(int argc, char **argv, std::string *error) {
@@ -39,6 +59,79 @@ bool ArgParser::parse(int argc, char **argv, std::string *error) {
return false; return false;
} }
if (token.rfind("--", 0) != 0 && token.rfind("-", 0) == 0) {
std::string short_key = token.substr(1);
std::string short_value;
size_t short_eq = short_key.find('=');
if (short_eq != std::string::npos) {
short_value = short_key.substr(short_eq + 1);
short_key = short_key.substr(0, short_eq);
}
auto sk = short_to_long_.find(short_key);
if (sk == short_to_long_.end()) {
if (error) {
*error = "Unknown option: -" + short_key;
}
return false;
}
auto m = meta_.find(sk->second);
if (m == meta_.end()) {
if (error) {
*error = "Unknown option: -" + short_key;
}
return false;
}
if (m->second.type == OptionType::kFlag) {
if (short_eq != std::string::npos) {
if (error) {
*error = "Flag does not take a value: -" + short_key;
}
return false;
}
flag_values_[sk->second] = true;
provided_[sk->second] = true;
} else if (m->second.type == OptionType::kString) {
if (short_eq == std::string::npos) {
if (i + 1 >= argc) {
if (error) {
*error = "Missing value for -" + short_key;
}
return false;
}
short_value = argv[++i];
}
string_values_[sk->second] = short_value;
provided_[sk->second] = true;
} else if (m->second.type == OptionType::kInt) {
long parsed;
if (short_eq == std::string::npos) {
if (i + 1 >= argc) {
if (error) {
*error = "Missing value for -" + short_key;
}
return false;
}
short_value = argv[++i];
}
errno = 0;
char *endp = nullptr;
parsed = std::strtol(short_value.c_str(), &endp, 0);
if (errno != 0 || endp == short_value.c_str() || *endp != '\0' ||
parsed < INT_MIN || parsed > INT_MAX) {
if (error) {
*error = "Invalid integer for -" + short_key + ": " + short_value;
}
return false;
}
int_values_[sk->second] = static_cast<int>(parsed);
provided_[sk->second] = true;
}
continue;
}
if (token.rfind("--", 0) != 0) { if (token.rfind("--", 0) != 0) {
if (error) { if (error) {
*error = "Unexpected positional argument: " + token; *error = "Unexpected positional argument: " + token;
@@ -51,13 +144,6 @@ bool ArgParser::parse(int argc, char **argv, std::string *error) {
size_t eq = token.find('='); size_t eq = token.find('=');
if (eq == std::string::npos) { if (eq == std::string::npos) {
key = token.substr(2); key = token.substr(2);
if (i + 1 >= argc) {
if (error) {
*error = "Missing value for --" + key;
}
return false;
}
value = argv[++i];
} else { } else {
key = token.substr(2, eq - 2); key = token.substr(2, eq - 2);
value = token.substr(eq + 1); value = token.substr(eq + 1);
@@ -71,10 +157,32 @@ bool ArgParser::parse(int argc, char **argv, std::string *error) {
return false; return false;
} }
if (m->second.type == OptionType::kFlag) {
if (eq != std::string::npos) {
if (error) {
*error = "Flag does not take a value: --" + key;
}
return false;
}
flag_values_[key] = true;
provided_[key] = true;
continue;
}
if (eq == std::string::npos) {
if (i + 1 >= argc) {
if (error) {
*error = "Missing value for --" + key;
}
return false;
}
value = argv[++i];
}
if (m->second.type == OptionType::kString) { if (m->second.type == OptionType::kString) {
string_values_[key] = value; string_values_[key] = value;
provided_[key] = true; provided_[key] = true;
} else { } else if (m->second.type == OptionType::kInt) {
errno = 0; errno = 0;
char *endp = nullptr; char *endp = nullptr;
long parsed = std::strtol(value.c_str(), &endp, 0); long parsed = std::strtol(value.c_str(), &endp, 0);
@@ -124,6 +232,14 @@ int ArgParser::getInt(const std::string &name) const {
return it->second; return it->second;
} }
bool ArgParser::getFlag(const std::string &name) const {
auto it = flag_values_.find(name);
if (it == flag_values_.end()) {
return false;
}
return it->second;
}
std::string ArgParser::helpText() const { std::string ArgParser::helpText() const {
std::ostringstream oss; std::ostringstream oss;
oss << "Usage: " << program_name_ << " [options]\n\n"; oss << "Usage: " << program_name_ << " [options]\n\n";
@@ -135,7 +251,16 @@ std::string ArgParser::helpText() const {
continue; continue;
} }
oss << " --" << key << " <value>"; oss << " ";
if (!m->second.short_name.empty()) {
oss << "-" << m->second.short_name << ", ";
} else {
oss << " ";
}
oss << "--" << key;
if (m->second.type != OptionType::kFlag) {
oss << " <value>";
}
if (m->second.required) { if (m->second.required) {
oss << " (required)"; oss << " (required)";
} }
@@ -147,11 +272,13 @@ std::string ArgParser::helpText() const {
oss << " [default: '" << s->second << "']"; oss << " [default: '" << s->second << "']";
} }
} else { } else {
if (m->second.type == OptionType::kInt) {
auto iv = int_values_.find(key); auto iv = int_values_.find(key);
if (iv != int_values_.end()) { if (iv != int_values_.end()) {
oss << " [default: " << iv->second << "]"; oss << " [default: " << iv->second << "]";
} }
} }
}
oss << "\n"; oss << "\n";
} }
return oss.str(); return oss.str();

View File

@@ -26,31 +26,40 @@ public:
void addString(const std::string &name, void addString(const std::string &name,
const std::string &default_value, const std::string &default_value,
const std::string &help, const std::string &help,
bool required = false); bool required = false,
const std::string &short_name = "");
void addInt(const std::string &name, void addInt(const std::string &name,
int default_value, int default_value,
const std::string &help, const std::string &help,
bool required = false); bool required = false,
const std::string &short_name = "");
void addFlag(const std::string &name,
const std::string &help,
const std::string &short_name = "");
bool parse(int argc, char **argv, std::string *error); bool parse(int argc, char **argv, std::string *error);
bool has(const std::string &name) const; bool has(const std::string &name) const;
std::string getString(const std::string &name) const; std::string getString(const std::string &name) const;
int getInt(const std::string &name) const; int getInt(const std::string &name) const;
bool getFlag(const std::string &name) const;
std::string helpText() const; std::string helpText() const;
private: private:
enum class OptionType { enum class OptionType {
kString, kString,
kInt kInt,
kFlag
}; };
struct OptionMeta { struct OptionMeta {
OptionType type; OptionType type;
std::string help; std::string help;
bool required; bool required;
std::string short_name;
}; };
std::string program_name_; std::string program_name_;
@@ -59,5 +68,7 @@ private:
std::unordered_map<std::string, std::string> string_values_; std::unordered_map<std::string, std::string> string_values_;
std::unordered_map<std::string, int> int_values_; std::unordered_map<std::string, int> int_values_;
std::unordered_map<std::string, bool> flag_values_;
std::unordered_map<std::string, bool> provided_; std::unordered_map<std::string, bool> provided_;
std::unordered_map<std::string, std::string> short_to_long_;
}; };

View File

@@ -5,37 +5,74 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
void write(DigilentJtag &jtag, uint32_t addr, uint8_t data, bool reset){ static constexpr uint8_t OP_NOP = 0x00;
uint8_t d[6], din[6]; static constexpr uint8_t OP_RESET_ON = 0x10;
d[0] = data; static constexpr uint8_t OP_RESET_OFF = 0x11;
d[1] = (uint8_t)addr; static constexpr uint8_t OP_WRITE8 = 0x20;
d[2] = (uint8_t)(addr>>8); static constexpr uint8_t OP_READ8 = 0x21;
d[3] = (uint8_t)(addr>>16); static constexpr uint8_t OP_WRITE32 = 0x22;
d[4] = (uint8_t)(addr>>24); static constexpr uint8_t OP_READ32 = 0x23;
d[5] = ((reset) ? 1 : 0) | 0x2; // <we><rst> static constexpr uint8_t OP_PING = 0x30;
static constexpr uint8_t OP_CLEAR_FLAGS = 0x40;
jtag.shiftData(d, din, 42); static void shift72(DigilentJtag &jtag, const uint8_t tx[9], uint8_t rx[9]) {
jtag.shiftData(tx, rx, 72);
} }
uint8_t read(DigilentJtag &jtag, uint32_t addr, bool reset){ static void make_cmd(uint8_t out[9], uint8_t opcode, uint32_t addr, uint32_t data) {
uint8_t d[6], din[6]; out[0] = (uint8_t)data;
d[0] = 0xff; out[1] = (uint8_t)(data >> 8);
d[1] = (uint8_t)addr; out[2] = (uint8_t)(data >> 16);
d[2] = (uint8_t)(addr>>8); out[3] = (uint8_t)(data >> 24);
d[3] = (uint8_t)(addr>>16); out[4] = (uint8_t)addr;
d[4] = (uint8_t)(addr>>24); out[5] = (uint8_t)(addr >> 8);
d[5] = ((reset) ? 1 : 0); // <we><rst> out[6] = (uint8_t)(addr >> 16);
out[7] = (uint8_t)(addr >> 24);
out[8] = opcode;
}
jtag.shiftData(d, din, 42); static uint32_t get_data32(const uint8_t rx[9]) {
// Read back return ((uint32_t)rx[2]) |
jtag.shiftData(d, din, 42); ((uint32_t)rx[3] << 8) |
((uint32_t)rx[4] << 16) |
((uint32_t)rx[5] << 24);
}
return din[0]; static uint32_t do_cmd32(DigilentJtag& jtag, uint8_t opcode, uint32_t addr, uint32_t data){
uint8_t tx[9], rx[9];
make_cmd(tx, opcode, addr, data);
shift72(jtag, tx, rx);
for(int i=0; i<32; i++){
make_cmd(tx, OP_NOP, 0, 0);
shift72(jtag, tx, rx);
if(rx[0] == opcode){
return get_data32(rx);
}
}
printf("Could not do command\r\n");
return 0;
}
static inline void write8(DigilentJtag& jtag, uint32_t addr, uint8_t value) {
(void)do_cmd32(jtag, OP_WRITE8, addr, value);
}
static inline uint8_t read8(DigilentJtag& jtag, uint32_t addr) {
return (uint8_t)do_cmd32(jtag, OP_READ8, addr, 0);
}
static inline void write32(DigilentJtag& jtag, uint32_t addr, uint32_t value) {
(void)do_cmd32(jtag, OP_WRITE32, addr, value);
}
static inline uint32_t read32(DigilentJtag& jtag, uint32_t addr) {
return do_cmd32(jtag, OP_READ32, addr, 0);
} }
int main(int argc, char** argv){ int main(int argc, char** argv){
ArgParser parser(argc > 0 ? argv[0] : "test"); ArgParser parser(argc > 0 ? argv[0] : "test");
parser.addString("write", "", "file to write"); parser.addString("file", "", "File to write", true, "f");
parser.addFlag("verify", "Verify", "v");
std::string parse_error; std::string parse_error;
if (!parser.parse(argc, argv, &parse_error)) { if (!parser.parse(argc, argv, &parse_error)) {
@@ -48,8 +85,6 @@ int main(int argc, char** argv){
return -1; return -1;
} }
const std::string arg_write = parser.getString("write");
DigilentJtag jtag; DigilentJtag jtag;
if(!jtag.open()){ if(!jtag.open()){
printf("Could not open programmer\r\n"); printf("Could not open programmer\r\n");
@@ -57,34 +92,51 @@ int main(int argc, char** argv){
} }
jtag.setChain(1); jtag.setChain(1);
// Start reset
read(jtag, 0, true);
if(arg_write!=""){ do_cmd32(jtag, OP_CLEAR_FLAGS, 0, 0);
uint32_t addr = 0; // Check for ping
uint8_t buf[32]; if((do_cmd32(jtag, OP_PING, 0, 0) & 0xffu) != 0xa5u){
int nr; printf("PING response was not right\r\n");
FILE* f = fopen(arg_write.c_str(), "rb"); jtag.close();
return -1;
}
const std::string file = parser.getString("file");
FILE* f = fopen(file.c_str(), "rb");
if(!f){ if(!f){
goto end; printf("Could not open file\r\n");
jtag.close();
return -1;
} }
do_cmd32(jtag, OP_RESET_ON, 0, 0);
int nr = 0;
int addr = 0;
do{ do{
nr = fread(buf, 1, 32, f); uint32_t buf[32];
for(int i=0; i<32; i++){ nr = fread(buf, sizeof(uint32_t), 32, f);
write(jtag, addr, buf[i], true); for(int i=0; i<nr; i++){
addr++; write32(jtag, addr+(i*4), buf[i]);
printf(".");
} }
}while(nr>0); printf("\r\n");
if(parser.getFlag("verify")){
for(int i=0; i<nr; i++){
uint32_t r = read32(jtag, addr+(i*4));
if(r!=buf[i]){
printf(" -- Verify failed at %04x : %08x != %08x\r\n", addr+(i*4), r, buf[i]);
}
}
}
addr += nr*4;
}while(nr > 0);
do_cmd32(jtag, OP_RESET_OFF, 0, 0);
fclose(f); fclose(f);
}
end:
// End reset
read(jtag, 0, false);
jtag.close(); jtag.close();
return 0; return 0;
} }