Added some stuff from modem and added formal
This commit is contained in:
73
cores/wb/formal_checker/formal/formal_wb_master_checker.v
Normal file
73
cores/wb/formal_checker/formal/formal_wb_master_checker.v
Normal file
@@ -0,0 +1,73 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module formal_wb_master_checker (
|
||||
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,
|
||||
input wire i_wb_cyc,
|
||||
input wire [31:0] o_wb_rdt,
|
||||
input wire o_wb_ack
|
||||
);
|
||||
reg f_past_valid;
|
||||
|
||||
initial f_past_valid = 1'b0;
|
||||
|
||||
always @(posedge i_clk) begin
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
// A1: Slave ACK must correspond to either a same-cycle or previous-cycle request
|
||||
if(o_wb_ack)
|
||||
assume(
|
||||
(i_wb_cyc && i_wb_stb) ||
|
||||
(f_past_valid && $past(i_wb_cyc && i_wb_stb))
|
||||
);
|
||||
|
||||
// A2: Slave must not ACK outside an active cycle
|
||||
if(!i_wb_cyc)
|
||||
assume(!o_wb_ack);
|
||||
|
||||
// A3: Once STB has been low for a full cycle, slave ACK must be low
|
||||
if(
|
||||
f_past_valid &&
|
||||
!$past(i_wb_stb) &&
|
||||
!i_wb_stb
|
||||
)
|
||||
assume(!o_wb_ack);
|
||||
|
||||
// R1: Reset must leave the master initialized on the following cycle
|
||||
if(f_past_valid && $past(i_rst || i_wb_rst)) begin
|
||||
assert(!i_wb_cyc);
|
||||
assert(!i_wb_stb);
|
||||
end
|
||||
|
||||
// R2: STB never high without CYC
|
||||
if(i_wb_stb)
|
||||
assert(i_wb_cyc);
|
||||
|
||||
// R3: Once a request starts, hold it stable until the slave responds
|
||||
if(
|
||||
f_past_valid &&
|
||||
$past(i_wb_cyc && i_wb_stb && !o_wb_ack) &&
|
||||
!o_wb_ack &&
|
||||
!(i_rst || i_wb_rst)
|
||||
) begin
|
||||
assert(i_wb_cyc);
|
||||
assert(i_wb_stb);
|
||||
assert(i_wb_adr == $past(i_wb_adr));
|
||||
assert(i_wb_dat == $past(i_wb_dat));
|
||||
assert(i_wb_sel == $past(i_wb_sel));
|
||||
assert(i_wb_we == $past(i_wb_we));
|
||||
end
|
||||
|
||||
// R4: Once CYC is low, STB must also be low
|
||||
if(!i_wb_cyc)
|
||||
assert(!i_wb_stb);
|
||||
end
|
||||
|
||||
wire unused = &{1'b0, o_wb_rdt};
|
||||
endmodule
|
||||
68
cores/wb/formal_checker/formal/formal_wb_slave_checker.v
Normal file
68
cores/wb/formal_checker/formal/formal_wb_slave_checker.v
Normal file
@@ -0,0 +1,68 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module formal_wb_slave_checker (
|
||||
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,
|
||||
input wire i_wb_cyc,
|
||||
input wire [31:0] o_wb_rdt,
|
||||
input wire o_wb_ack
|
||||
);
|
||||
reg f_past_valid;
|
||||
|
||||
initial f_past_valid = 1'b0;
|
||||
|
||||
always @(posedge i_clk) begin
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
// A1: Reset forces cyc=0, stb=0
|
||||
if (i_rst) begin
|
||||
assume(!i_wb_cyc);
|
||||
assume(!i_wb_stb);
|
||||
end
|
||||
|
||||
// A2: std->cyc, stb never high without cyc
|
||||
if(i_wb_stb)
|
||||
assume(i_wb_cyc);
|
||||
|
||||
// A3: once a request starts, hold it stable until the slave responds
|
||||
if(f_past_valid && $past(i_wb_cyc && i_wb_stb && !o_wb_ack)) begin
|
||||
assume(i_wb_cyc);
|
||||
assume(i_wb_stb);
|
||||
assume(i_wb_adr == $past(i_wb_adr));
|
||||
assume(i_wb_dat == $past(i_wb_dat));
|
||||
assume(i_wb_sel == $past(i_wb_sel));
|
||||
assume(i_wb_we == $past(i_wb_we));
|
||||
end
|
||||
|
||||
// R1: ACK must correspond to either a same-cycle or previous-cycle request
|
||||
if(o_wb_ack)
|
||||
assert(
|
||||
(i_wb_cyc && i_wb_stb) ||
|
||||
(f_past_valid && $past(i_wb_cyc && i_wb_stb))
|
||||
);
|
||||
|
||||
// R2: !CYC->!ACK : no ghost acks
|
||||
if(!i_wb_cyc)
|
||||
assert(!o_wb_ack);
|
||||
|
||||
// R3: Reset must leave the slave initialized on the following cycle
|
||||
if(f_past_valid && $past(i_rst || i_wb_rst))
|
||||
assert(!o_wb_ack);
|
||||
|
||||
// R4: once STB has been dropped for a full cycle, ACK must be low
|
||||
if(
|
||||
f_past_valid &&
|
||||
!$past(i_wb_stb) &&
|
||||
!i_wb_stb
|
||||
)
|
||||
assert(!o_wb_ack);
|
||||
end
|
||||
|
||||
wire unused = &{1'b0, o_wb_rdt};
|
||||
endmodule
|
||||
16
cores/wb/formal_checker/formal_checker.core
Normal file
16
cores/wb/formal_checker/formal_checker.core
Normal file
@@ -0,0 +1,16 @@
|
||||
CAPI=2:
|
||||
|
||||
name: joppeb:wb:formal_checker:1.0
|
||||
description: Reusable formal Wishbone protocol checkers
|
||||
|
||||
filesets:
|
||||
formal_rtl:
|
||||
files:
|
||||
- formal/formal_wb_slave_checker.v
|
||||
- formal/formal_wb_master_checker.v
|
||||
file_type: verilogSource
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- formal_rtl
|
||||
63
cores/wb/jtag_wb_bridbe/formal/formal_jtag_wb_bridge.v
Normal file
63
cores/wb/jtag_wb_bridbe/formal/formal_jtag_wb_bridge.v
Normal file
@@ -0,0 +1,63 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module formal_jtag_wb_bridge;
|
||||
(* gclk *) reg i_clk;
|
||||
(* anyseq *) reg i_rst;
|
||||
(* anyseq *) reg [31:0] i_wb_rdt;
|
||||
(* anyseq *) reg i_wb_ack;
|
||||
reg f_past_valid;
|
||||
|
||||
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;
|
||||
wire o_cmd_reset;
|
||||
|
||||
// This bridge has no dedicated Wishbone reset output.
|
||||
wire f_wb_rst = 1'b0;
|
||||
|
||||
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)
|
||||
);
|
||||
|
||||
formal_wb_master_checker wb_checker (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_wb_rst(f_wb_rst),
|
||||
.i_wb_adr(o_wb_adr),
|
||||
.i_wb_dat(o_wb_dat),
|
||||
.i_wb_sel(o_wb_sel),
|
||||
.i_wb_we(o_wb_we),
|
||||
.i_wb_stb(o_wb_stb),
|
||||
.i_wb_cyc(o_wb_cyc),
|
||||
.o_wb_rdt(i_wb_rdt),
|
||||
.o_wb_ack(i_wb_ack)
|
||||
);
|
||||
|
||||
initial f_past_valid = 1'b0;
|
||||
|
||||
always @(posedge i_clk) begin
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
// A1: Start in reset so the bridge state is initialized
|
||||
if (!f_past_valid)
|
||||
assume(i_rst);
|
||||
end
|
||||
|
||||
wire unused = &{1'b0, o_cmd_reset};
|
||||
endmodule
|
||||
14
cores/wb/jtag_wb_bridbe/formal/jtag_wb_bridge.sby
Normal file
14
cores/wb/jtag_wb_bridbe/formal/jtag_wb_bridge.sby
Normal file
@@ -0,0 +1,14 @@
|
||||
[options]
|
||||
mode prove
|
||||
depth 8
|
||||
|
||||
[engines]
|
||||
smtbmc z3
|
||||
|
||||
[script]
|
||||
{{"-formal"|gen_reads}}
|
||||
prep -top {{top_level}}
|
||||
clk2fflogic
|
||||
|
||||
[files]
|
||||
{{files}}
|
||||
63
cores/wb/jtag_wb_bridbe/formal/stub_cdc_req_resp.v
Normal file
63
cores/wb/jtag_wb_bridbe/formal/stub_cdc_req_resp.v
Normal file
@@ -0,0 +1,63 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module cdc_req_resp #(
|
||||
parameter integer REQ_W = 32,
|
||||
parameter integer RESP_W = 32,
|
||||
parameter integer STABLE_SAMPLES = 2
|
||||
)(
|
||||
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,
|
||||
input wire b_clk,
|
||||
input wire b_rst,
|
||||
output reg b_req_pulse,
|
||||
output reg [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
|
||||
);
|
||||
(* anyseq *) reg f_req_pulse;
|
||||
(* anyseq *) reg [REQ_W-1:0] f_req_data;
|
||||
reg f_past_valid;
|
||||
|
||||
assign a_req_busy = 1'b0;
|
||||
assign a_req_accepted = 1'b0;
|
||||
assign a_resp_pulse = 1'b0;
|
||||
assign a_resp_data = {RESP_W{1'b0}};
|
||||
assign b_resp_busy = 1'b0;
|
||||
assign b_resp_accepted = 1'b0;
|
||||
|
||||
initial f_past_valid = 1'b0;
|
||||
|
||||
always @(posedge b_clk) begin
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
b_req_pulse <= f_req_pulse;
|
||||
b_req_data <= f_req_data;
|
||||
|
||||
// A1: no abstract request while system reset is asserted
|
||||
if (b_rst)
|
||||
assume(!f_req_pulse);
|
||||
|
||||
// A2: abstract requests are single-cycle pulses
|
||||
if (f_past_valid && $past(f_req_pulse))
|
||||
assume(!f_req_pulse);
|
||||
end
|
||||
|
||||
wire unused = &{
|
||||
1'b0,
|
||||
a_clk,
|
||||
a_rst,
|
||||
a_req_pulse,
|
||||
a_req_data,
|
||||
b_resp_pulse,
|
||||
b_resp_data,
|
||||
STABLE_SAMPLES[0]
|
||||
};
|
||||
endmodule
|
||||
28
cores/wb/jtag_wb_bridbe/formal/stub_jtag_if.v
Normal file
28
cores/wb/jtag_wb_bridbe/formal/stub_jtag_if.v
Normal file
@@ -0,0 +1,28 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
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;
|
||||
|
||||
wire unused = &{1'b0, i_tdo, chain[0]};
|
||||
endmodule
|
||||
64
cores/wb/jtag_wb_bridbe/jtag_wb_bridge.core
Normal file
64
cores/wb/jtag_wb_bridbe/jtag_wb_bridge.core
Normal file
@@ -0,0 +1,64 @@
|
||||
CAPI=2:
|
||||
|
||||
name: joppeb:wb:jtag_wb_bridge:1.0
|
||||
description: Generic JTAG boundary scan to Wishbone classic bridge
|
||||
|
||||
filesets:
|
||||
rtl:
|
||||
depend:
|
||||
- joppeb:primitive:jtag_if
|
||||
- joppeb:util:cdc
|
||||
files:
|
||||
- rtl/jtag_wb_bridge.v
|
||||
file_type: verilogSource
|
||||
formal_rtl:
|
||||
depend:
|
||||
- joppeb:wb:formal_checker
|
||||
files:
|
||||
- formal/formal_jtag_wb_bridge.v
|
||||
file_type: verilogSource
|
||||
formal_abstract_rtl:
|
||||
depend:
|
||||
- joppeb:wb:formal_checker
|
||||
files:
|
||||
- rtl/jtag_wb_bridge.v
|
||||
- formal/stub_jtag_if.v
|
||||
- formal/stub_cdc_req_resp.v
|
||||
- formal/formal_jtag_wb_bridge.v
|
||||
file_type: verilogSource
|
||||
formal_cfg:
|
||||
files:
|
||||
- formal/jtag_wb_bridge.sby
|
||||
file_type: sbyConfigTemplate
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- rtl
|
||||
toplevel: jtag_wb_bridge
|
||||
parameters:
|
||||
- chain
|
||||
- byte_aligned
|
||||
formal:
|
||||
default_tool: symbiyosys
|
||||
filesets:
|
||||
- rtl
|
||||
- formal_rtl
|
||||
- formal_cfg
|
||||
toplevel: formal_jtag_wb_bridge
|
||||
formal_abstract:
|
||||
default_tool: symbiyosys
|
||||
filesets:
|
||||
- formal_abstract_rtl
|
||||
- formal_cfg
|
||||
toplevel: formal_jtag_wb_bridge
|
||||
|
||||
parameters:
|
||||
chain:
|
||||
datatype: int
|
||||
description: User chain
|
||||
paramtype: vlogparam
|
||||
byte_aligned:
|
||||
datatype: int
|
||||
description: use addr[1:0] for byte lane on 32-bit WB when 0, always use lane 0 when 1
|
||||
paramtype: vlogparam
|
||||
538
cores/wb/jtag_wb_bridbe/rtl/jtag_wb_bridge.v
Normal file
538
cores/wb/jtag_wb_bridbe/rtl/jtag_wb_bridge.v
Normal file
@@ -0,0 +1,538 @@
|
||||
`timescale 1 ns/1 ps
|
||||
|
||||
module jtag_wb_bridge #(
|
||||
parameter integer chain = 1,
|
||||
// 0: use addr[1:0] for byte lane on 32-bit WB
|
||||
// 1: always use lane 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 (Spartan-6 BSCAN wrapper)
|
||||
// ===========================================================================
|
||||
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;
|
||||
|
||||
localparam integer JTAG_DR_W = 72;
|
||||
|
||||
// 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 #(
|
||||
.chain(chain)
|
||||
) u_jtag (
|
||||
.i_tdo(jtag_shreg[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)
|
||||
);
|
||||
|
||||
wire jtag_async_reset = jtag_reset || i_rst;
|
||||
|
||||
// ===========================================================================
|
||||
// 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
|
||||
jtag_shreg <= {JTAG_DR_W{1'b0}};
|
||||
resp_hold_tck <= {JTAG_DR_W{1'b0}};
|
||||
end else begin
|
||||
// Latch new response word from CDC when it arrives (independent of CAPTURE)
|
||||
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
|
||||
|
||||
// ===========================================================================
|
||||
// System domain: Wishbone master + small command queue + response pending
|
||||
// ===========================================================================
|
||||
// Opcodes
|
||||
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_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
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
if (i_rst) begin
|
||||
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_level_r<= 1'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
|
||||
b_resp_pulse <= 1'b0;
|
||||
|
||||
// Sync req-busy level (reporting only)
|
||||
req_busy_sync1 <= a_req_busy;
|
||||
req_busy_sync2 <= req_busy_sync1;
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Accept incoming command from CDC (always delivered; we buffer internally)
|
||||
// -----------------------------------------------------------------------
|
||||
if (b_req_pulse) begin
|
||||
// assign a sequence number to each received command
|
||||
cmd_seq_r <= cmd_seq_r + 8'd1;
|
||||
|
||||
// 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
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Wishbone completion -> create response (but don't drop; buffer pending)
|
||||
// -----------------------------------------------------------------------
|
||||
if (wb_busy && i_wb_ack) begin
|
||||
wb_busy <= 1'b0;
|
||||
wb_we_r <= 1'b0;
|
||||
|
||||
// Determine response data
|
||||
case (act_opcode)
|
||||
OP_READ8: begin
|
||||
resp_pending_word <= pack_resp(
|
||||
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
|
||||
resp_pending <= 1'b1;
|
||||
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
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 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
|
||||
6
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/.gitignore
vendored
Normal file
6
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
__pycache__/
|
||||
*.d
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
prog
|
||||
43
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/Makefile
Normal file
43
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/Makefile
Normal file
@@ -0,0 +1,43 @@
|
||||
TOOLCHAIN_PREFIX ?=
|
||||
|
||||
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||
AR := $(TOOLCHAIN_PREFIX)ar
|
||||
|
||||
TARGET := prog
|
||||
STATIC_LIB := libjtag_wb_bridge.a
|
||||
SHARED_LIB := libjtag_wb_bridge.so
|
||||
|
||||
ADEPT_LIBDIR := /opt/packages/digilent.adept.runtime_2.27.9-x86_64/lib64
|
||||
|
||||
CPPFLAGS := -MMD -MP
|
||||
CXXFLAGS := -O2 -Wall -Wextra -std=c++17 -fPIC
|
||||
LDFLAGS := -L$(ADEPT_LIBDIR) -Wl,--disable-new-dtags -Wl,-rpath,$(ADEPT_LIBDIR)
|
||||
LIBS := -ldjtg -ldmgr -ldpcomm -ldabs -ldftd2xx
|
||||
|
||||
LIB_SRCS := difilent_jtag.cpp jtag_wb_bridge_client.cpp jtag_wb_bridge_c.cpp
|
||||
APP_SRCS := prog.cpp argparse.cpp
|
||||
|
||||
LIB_OBJS := $(LIB_SRCS:.cpp=.o)
|
||||
APP_OBJS := $(APP_SRCS:.cpp=.o)
|
||||
DEPS := $(LIB_OBJS:.o=.d) $(APP_OBJS:.o=.d)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(STATIC_LIB) $(SHARED_LIB) $(TARGET)
|
||||
|
||||
$(STATIC_LIB): $(LIB_OBJS)
|
||||
$(AR) rcs $@ $(LIB_OBJS)
|
||||
|
||||
$(SHARED_LIB): $(LIB_OBJS)
|
||||
$(CXX) -shared $(LDFLAGS) -o $@ $(LIB_OBJS) $(LIBS)
|
||||
|
||||
$(TARGET): $(APP_OBJS) $(STATIC_LIB)
|
||||
$(CXX) $(LDFLAGS) -o $@ $(APP_OBJS) -L. -ljtag_wb_bridge $(LIBS)
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) $(STATIC_LIB) $(SHARED_LIB) $(LIB_OBJS) $(APP_OBJS) $(DEPS)
|
||||
|
||||
-include $(DEPS)
|
||||
285
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/argparse.cpp
Normal file
285
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/argparse.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
#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,
|
||||
const std::string &short_name) {
|
||||
order_.push_back(name);
|
||||
meta_[name] = {OptionType::kString, help, required, short_name};
|
||||
string_values_[name] = default_value;
|
||||
provided_[name] = false;
|
||||
if (!short_name.empty()) {
|
||||
short_to_long_[short_name] = name;
|
||||
}
|
||||
}
|
||||
|
||||
void ArgParser::addInt(const std::string &name,
|
||||
int default_value,
|
||||
const std::string &help,
|
||||
bool required,
|
||||
const std::string &short_name) {
|
||||
order_.push_back(name);
|
||||
meta_[name] = {OptionType::kInt, help, required, short_name};
|
||||
int_values_[name] = default_value;
|
||||
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) {
|
||||
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 && 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 (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);
|
||||
} 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::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) {
|
||||
string_values_[key] = value;
|
||||
provided_[key] = true;
|
||||
} else if (m->second.type == OptionType::kInt) {
|
||||
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;
|
||||
}
|
||||
|
||||
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::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 << " ";
|
||||
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) {
|
||||
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 {
|
||||
if (m->second.type == OptionType::kInt) {
|
||||
auto iv = int_values_.find(key);
|
||||
if (iv != int_values_.end()) {
|
||||
oss << " [default: " << iv->second << "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
oss << "\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
74
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/argparse.hpp
Normal file
74
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/argparse.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#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,
|
||||
const std::string &short_name = "");
|
||||
|
||||
void addInt(const std::string &name,
|
||||
int default_value,
|
||||
const std::string &help,
|
||||
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 has(const std::string &name) const;
|
||||
std::string getString(const std::string &name) const;
|
||||
int getInt(const std::string &name) const;
|
||||
bool getFlag(const std::string &name) const;
|
||||
|
||||
std::string helpText() const;
|
||||
|
||||
private:
|
||||
enum class OptionType {
|
||||
kString,
|
||||
kInt,
|
||||
kFlag
|
||||
};
|
||||
|
||||
struct OptionMeta {
|
||||
OptionType type;
|
||||
std::string help;
|
||||
bool required;
|
||||
std::string short_name;
|
||||
};
|
||||
|
||||
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> flag_values_;
|
||||
std::unordered_map<std::string, bool> provided_;
|
||||
std::unordered_map<std::string, std::string> short_to_long_;
|
||||
};
|
||||
276
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/difilent_jtag.cpp
Normal file
276
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/difilent_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;
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
142
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/jtag_bridge.py
Normal file
142
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/jtag_bridge.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import ctypes
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _load_library(path=None):
|
||||
if path is None:
|
||||
env_path = os.environ.get("JTAG_BRIDGE_LIB")
|
||||
if env_path:
|
||||
path = env_path
|
||||
else:
|
||||
path = Path(__file__).with_name("libjtag_wb_bridge.so")
|
||||
return ctypes.CDLL(str(path))
|
||||
|
||||
|
||||
class JtagBridgeError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class JtagBridge:
|
||||
def __init__(self, library_path=None):
|
||||
self._handle = None
|
||||
self._lib = _load_library(library_path)
|
||||
self._configure()
|
||||
self._handle = self._lib.jtag_bridge_create()
|
||||
if not self._handle:
|
||||
raise JtagBridgeError("failed to allocate bridge handle")
|
||||
|
||||
def _configure(self):
|
||||
self._lib.jtag_bridge_create.restype = ctypes.c_void_p
|
||||
self._lib.jtag_bridge_destroy.argtypes = [ctypes.c_void_p]
|
||||
self._lib.jtag_bridge_open.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
|
||||
self._lib.jtag_bridge_open.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_open_selector.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_char_p,
|
||||
ctypes.c_int,
|
||||
ctypes.c_int,
|
||||
]
|
||||
self._lib.jtag_bridge_open_selector.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_close.argtypes = [ctypes.c_void_p]
|
||||
self._lib.jtag_bridge_set_speed.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_uint32,
|
||||
ctypes.POINTER(ctypes.c_uint32),
|
||||
]
|
||||
self._lib.jtag_bridge_set_speed.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_set_chain.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
||||
self._lib.jtag_bridge_set_chain.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_clear_flags.argtypes = [ctypes.c_void_p]
|
||||
self._lib.jtag_bridge_clear_flags.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_ping.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8)]
|
||||
self._lib.jtag_bridge_ping.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_set_reset.argtypes = [ctypes.c_void_p, ctypes.c_int]
|
||||
self._lib.jtag_bridge_set_reset.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_write8.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint8]
|
||||
self._lib.jtag_bridge_write8.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_read8.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_uint32,
|
||||
ctypes.POINTER(ctypes.c_uint8),
|
||||
]
|
||||
self._lib.jtag_bridge_read8.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_write32.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32]
|
||||
self._lib.jtag_bridge_write32.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_read32.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_uint32,
|
||||
ctypes.POINTER(ctypes.c_uint32),
|
||||
]
|
||||
self._lib.jtag_bridge_read32.restype = ctypes.c_int
|
||||
self._lib.jtag_bridge_last_error.argtypes = [ctypes.c_void_p]
|
||||
self._lib.jtag_bridge_last_error.restype = ctypes.c_char_p
|
||||
|
||||
def close(self):
|
||||
if self._handle:
|
||||
self._lib.jtag_bridge_close(self._handle)
|
||||
|
||||
def destroy(self):
|
||||
if self._handle:
|
||||
self._lib.jtag_bridge_destroy(self._handle)
|
||||
self._handle = None
|
||||
|
||||
def __del__(self):
|
||||
self.destroy()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
self.destroy()
|
||||
return False
|
||||
|
||||
def _check(self, ok):
|
||||
if not ok:
|
||||
message = self._lib.jtag_bridge_last_error(self._handle)
|
||||
raise JtagBridgeError(message.decode("utf-8"))
|
||||
|
||||
def open(self, port=0, chain=1):
|
||||
self._check(self._lib.jtag_bridge_open(self._handle, port, chain))
|
||||
|
||||
def open_selector(self, selector, port=0, chain=1):
|
||||
self._check(
|
||||
self._lib.jtag_bridge_open_selector(
|
||||
self._handle, selector.encode("utf-8"), port, chain
|
||||
)
|
||||
)
|
||||
|
||||
def set_speed(self, requested_hz):
|
||||
actual = ctypes.c_uint32()
|
||||
self._check(self._lib.jtag_bridge_set_speed(self._handle, requested_hz, ctypes.byref(actual)))
|
||||
return actual.value
|
||||
|
||||
def set_chain(self, chain):
|
||||
self._check(self._lib.jtag_bridge_set_chain(self._handle, chain))
|
||||
|
||||
def clear_flags(self):
|
||||
self._check(self._lib.jtag_bridge_clear_flags(self._handle))
|
||||
|
||||
def ping(self):
|
||||
value = ctypes.c_uint8()
|
||||
self._check(self._lib.jtag_bridge_ping(self._handle, ctypes.byref(value)))
|
||||
return value.value
|
||||
|
||||
def set_reset(self, enabled):
|
||||
self._check(self._lib.jtag_bridge_set_reset(self._handle, int(bool(enabled))))
|
||||
|
||||
def write8(self, addr, value):
|
||||
self._check(self._lib.jtag_bridge_write8(self._handle, addr, value))
|
||||
|
||||
def read8(self, addr):
|
||||
value = ctypes.c_uint8()
|
||||
self._check(self._lib.jtag_bridge_read8(self._handle, addr, ctypes.byref(value)))
|
||||
return value.value
|
||||
|
||||
def write32(self, addr, value):
|
||||
self._check(self._lib.jtag_bridge_write32(self._handle, addr, value))
|
||||
|
||||
def read32(self, addr):
|
||||
value = ctypes.c_uint32()
|
||||
self._check(self._lib.jtag_bridge_read32(self._handle, addr, ctypes.byref(value)))
|
||||
return value.value
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "jtag_wb_bridge_c.h"
|
||||
|
||||
#include "jtag_wb_bridge_client.hpp"
|
||||
|
||||
#include <new>
|
||||
|
||||
struct JtagBridgeHandle {
|
||||
JtagWishboneBridge bridge;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename Fn>
|
||||
int callBridge(JtagBridgeHandle* handle, Fn&& fn) {
|
||||
if (!handle) {
|
||||
return 0;
|
||||
}
|
||||
return fn(handle->bridge) ? 1 : 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
JtagBridgeHandle* jtag_bridge_create(void) {
|
||||
return new (std::nothrow) JtagBridgeHandle();
|
||||
}
|
||||
|
||||
void jtag_bridge_destroy(JtagBridgeHandle* handle) {
|
||||
delete handle;
|
||||
}
|
||||
|
||||
int jtag_bridge_open(JtagBridgeHandle* handle, int port, int chain) {
|
||||
return callBridge(handle, [port, chain](JtagWishboneBridge& bridge) {
|
||||
return bridge.open(port, chain);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_open_selector(JtagBridgeHandle* handle, const char* selector, int port, int chain) {
|
||||
if (!handle || !selector) {
|
||||
return 0;
|
||||
}
|
||||
return handle->bridge.open(selector, port, chain) ? 1 : 0;
|
||||
}
|
||||
|
||||
void jtag_bridge_close(JtagBridgeHandle* handle) {
|
||||
if (handle) {
|
||||
handle->bridge.close();
|
||||
}
|
||||
}
|
||||
|
||||
int jtag_bridge_set_speed(JtagBridgeHandle* handle, uint32_t requested_hz, uint32_t* actual_hz) {
|
||||
return callBridge(handle, [requested_hz, actual_hz](JtagWishboneBridge& bridge) {
|
||||
return bridge.setSpeed(requested_hz, actual_hz);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_set_chain(JtagBridgeHandle* handle, int chain) {
|
||||
return callBridge(handle, [chain](JtagWishboneBridge& bridge) {
|
||||
return bridge.setChain(chain);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_clear_flags(JtagBridgeHandle* handle) {
|
||||
return callBridge(handle, [](JtagWishboneBridge& bridge) {
|
||||
return bridge.clearFlags();
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_ping(JtagBridgeHandle* handle, uint8_t* ping_value) {
|
||||
return callBridge(handle, [ping_value](JtagWishboneBridge& bridge) {
|
||||
return bridge.ping(ping_value);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_set_reset(JtagBridgeHandle* handle, int enabled) {
|
||||
return callBridge(handle, [enabled](JtagWishboneBridge& bridge) {
|
||||
return bridge.setReset(enabled != 0);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_write8(JtagBridgeHandle* handle, uint32_t addr, uint8_t value) {
|
||||
return callBridge(handle, [addr, value](JtagWishboneBridge& bridge) {
|
||||
return bridge.write8(addr, value);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_read8(JtagBridgeHandle* handle, uint32_t addr, uint8_t* value) {
|
||||
return callBridge(handle, [addr, value](JtagWishboneBridge& bridge) {
|
||||
return bridge.read8(addr, value);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_write32(JtagBridgeHandle* handle, uint32_t addr, uint32_t value) {
|
||||
return callBridge(handle, [addr, value](JtagWishboneBridge& bridge) {
|
||||
return bridge.write32(addr, value);
|
||||
});
|
||||
}
|
||||
|
||||
int jtag_bridge_read32(JtagBridgeHandle* handle, uint32_t addr, uint32_t* value) {
|
||||
return callBridge(handle, [addr, value](JtagWishboneBridge& bridge) {
|
||||
return bridge.read32(addr, value);
|
||||
});
|
||||
}
|
||||
|
||||
const char* jtag_bridge_last_error(const JtagBridgeHandle* handle) {
|
||||
if (!handle) {
|
||||
return "invalid bridge handle";
|
||||
}
|
||||
return handle->bridge.lastError().c_str();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct JtagBridgeHandle JtagBridgeHandle;
|
||||
|
||||
JtagBridgeHandle* jtag_bridge_create(void);
|
||||
void jtag_bridge_destroy(JtagBridgeHandle* handle);
|
||||
|
||||
int jtag_bridge_open(JtagBridgeHandle* handle, int port, int chain);
|
||||
int jtag_bridge_open_selector(JtagBridgeHandle* handle, const char* selector, int port, int chain);
|
||||
void jtag_bridge_close(JtagBridgeHandle* handle);
|
||||
|
||||
int jtag_bridge_set_speed(JtagBridgeHandle* handle, uint32_t requested_hz, uint32_t* actual_hz);
|
||||
int jtag_bridge_set_chain(JtagBridgeHandle* handle, int chain);
|
||||
|
||||
int jtag_bridge_clear_flags(JtagBridgeHandle* handle);
|
||||
int jtag_bridge_ping(JtagBridgeHandle* handle, uint8_t* ping_value);
|
||||
int jtag_bridge_set_reset(JtagBridgeHandle* handle, int enabled);
|
||||
|
||||
int jtag_bridge_write8(JtagBridgeHandle* handle, uint32_t addr, uint8_t value);
|
||||
int jtag_bridge_read8(JtagBridgeHandle* handle, uint32_t addr, uint8_t* value);
|
||||
int jtag_bridge_write32(JtagBridgeHandle* handle, uint32_t addr, uint32_t value);
|
||||
int jtag_bridge_read32(JtagBridgeHandle* handle, uint32_t addr, uint32_t* value);
|
||||
|
||||
const char* jtag_bridge_last_error(const JtagBridgeHandle* handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,181 @@
|
||||
#include "jtag_wb_bridge_client.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint8_t kOpNop = 0x00;
|
||||
constexpr uint8_t kOpResetOn = 0x10;
|
||||
constexpr uint8_t kOpResetOff = 0x11;
|
||||
constexpr uint8_t kOpWrite8 = 0x20;
|
||||
constexpr uint8_t kOpRead8 = 0x21;
|
||||
constexpr uint8_t kOpWrite32 = 0x22;
|
||||
constexpr uint8_t kOpRead32 = 0x23;
|
||||
constexpr uint8_t kOpPing = 0x30;
|
||||
constexpr uint8_t kOpClearFlags = 0x40;
|
||||
|
||||
constexpr int kPacketBytes = 9;
|
||||
constexpr int kPacketBits = 72;
|
||||
constexpr int kMaxPollAttempts = 32;
|
||||
|
||||
void makeCommand(uint8_t out[kPacketBytes], uint8_t opcode, uint32_t addr, uint32_t data) {
|
||||
out[0] = static_cast<uint8_t>(data);
|
||||
out[1] = static_cast<uint8_t>(data >> 8);
|
||||
out[2] = static_cast<uint8_t>(data >> 16);
|
||||
out[3] = static_cast<uint8_t>(data >> 24);
|
||||
out[4] = static_cast<uint8_t>(addr);
|
||||
out[5] = static_cast<uint8_t>(addr >> 8);
|
||||
out[6] = static_cast<uint8_t>(addr >> 16);
|
||||
out[7] = static_cast<uint8_t>(addr >> 24);
|
||||
out[8] = opcode;
|
||||
}
|
||||
|
||||
uint32_t getResponseData32(const uint8_t rx[kPacketBytes]) {
|
||||
return static_cast<uint32_t>(rx[2]) |
|
||||
(static_cast<uint32_t>(rx[3]) << 8) |
|
||||
(static_cast<uint32_t>(rx[4]) << 16) |
|
||||
(static_cast<uint32_t>(rx[5]) << 24);
|
||||
}
|
||||
|
||||
uint8_t getLastOpcode(const uint8_t rx[kPacketBytes]) {
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool JtagWishboneBridge::open(int port, int chain) {
|
||||
if (!jtag_.open(port)) {
|
||||
return setError(jtag_.lastError());
|
||||
}
|
||||
if (!jtag_.setChain(chain)) {
|
||||
const std::string msg = jtag_.lastError();
|
||||
jtag_.close();
|
||||
return setError(msg);
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::open(const std::string& selector, int port, int chain) {
|
||||
if (!jtag_.open(selector, port)) {
|
||||
return setError(jtag_.lastError());
|
||||
}
|
||||
if (!jtag_.setChain(chain)) {
|
||||
const std::string msg = jtag_.lastError();
|
||||
jtag_.close();
|
||||
return setError(msg);
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void JtagWishboneBridge::close() {
|
||||
jtag_.close();
|
||||
last_error_.clear();
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::isOpen() const {
|
||||
return jtag_.isOpen();
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::setSpeed(uint32_t requested_hz, uint32_t* actual_hz) {
|
||||
if (!jtag_.setSpeed(requested_hz, actual_hz)) {
|
||||
return setError(jtag_.lastError());
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::setChain(int chain) {
|
||||
if (!jtag_.setChain(chain)) {
|
||||
return setError(jtag_.lastError());
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::clearFlags() {
|
||||
return executeCommand(kOpClearFlags, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::ping(uint8_t* ping_value) {
|
||||
uint32_t response = 0;
|
||||
if (!executeCommand(kOpPing, 0, 0, &response)) {
|
||||
return false;
|
||||
}
|
||||
if (ping_value) {
|
||||
*ping_value = static_cast<uint8_t>(response & 0xffu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::setReset(bool enabled) {
|
||||
return executeCommand(enabled ? kOpResetOn : kOpResetOff, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::write8(uint32_t addr, uint8_t value) {
|
||||
return executeCommand(kOpWrite8, addr, value, nullptr);
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::read8(uint32_t addr, uint8_t* value) {
|
||||
uint32_t response = 0;
|
||||
if (!value) {
|
||||
return setError("read8: value pointer is null");
|
||||
}
|
||||
if (!executeCommand(kOpRead8, addr, 0, &response)) {
|
||||
return false;
|
||||
}
|
||||
*value = static_cast<uint8_t>(response & 0xffu);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::write32(uint32_t addr, uint32_t value) {
|
||||
return executeCommand(kOpWrite32, addr, value, nullptr);
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::read32(uint32_t addr, uint32_t* value) {
|
||||
if (!value) {
|
||||
return setError("read32: value pointer is null");
|
||||
}
|
||||
return executeCommand(kOpRead32, addr, 0, value);
|
||||
}
|
||||
|
||||
const std::string& JtagWishboneBridge::lastError() const {
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::executeCommand(uint8_t opcode, uint32_t addr, uint32_t data, uint32_t* response_data) {
|
||||
if (!jtag_.isOpen()) {
|
||||
return setError("executeCommand: device not open");
|
||||
}
|
||||
|
||||
uint8_t tx[kPacketBytes] = {0};
|
||||
uint8_t rx[kPacketBytes] = {0};
|
||||
makeCommand(tx, opcode, addr, data);
|
||||
if (!jtag_.shiftData(tx, rx, kPacketBits)) {
|
||||
return setError(jtag_.lastError());
|
||||
}
|
||||
|
||||
for (int i = 0; i < kMaxPollAttempts; ++i) {
|
||||
makeCommand(tx, kOpNop, 0, 0);
|
||||
if (!jtag_.shiftData(tx, rx, kPacketBits)) {
|
||||
return setError(jtag_.lastError());
|
||||
}
|
||||
if (getLastOpcode(rx) == opcode) {
|
||||
if (response_data) {
|
||||
*response_data = getResponseData32(rx);
|
||||
}
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
char msg[96];
|
||||
std::snprintf(msg, sizeof(msg), "command 0x%02x timed out after %d polls", opcode, kMaxPollAttempts);
|
||||
return setError(msg);
|
||||
}
|
||||
|
||||
bool JtagWishboneBridge::setError(const std::string& msg) {
|
||||
last_error_ = msg;
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "digilent_jtag.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class JtagWishboneBridge {
|
||||
public:
|
||||
JtagWishboneBridge() = default;
|
||||
~JtagWishboneBridge() = default;
|
||||
|
||||
JtagWishboneBridge(const JtagWishboneBridge&) = delete;
|
||||
JtagWishboneBridge& operator=(const JtagWishboneBridge&) = delete;
|
||||
|
||||
bool open(int port = 0, int chain = 1);
|
||||
bool open(const std::string& selector, int port = 0, int chain = 1);
|
||||
void close();
|
||||
|
||||
bool isOpen() const;
|
||||
|
||||
bool setSpeed(uint32_t requested_hz, uint32_t* actual_hz = nullptr);
|
||||
bool setChain(int chain);
|
||||
|
||||
bool clearFlags();
|
||||
bool ping(uint8_t* ping_value = nullptr);
|
||||
bool setReset(bool enabled);
|
||||
|
||||
bool write8(uint32_t addr, uint8_t value);
|
||||
bool read8(uint32_t addr, uint8_t* value);
|
||||
bool write32(uint32_t addr, uint32_t value);
|
||||
bool read32(uint32_t addr, uint32_t* value);
|
||||
|
||||
const std::string& lastError() const;
|
||||
|
||||
private:
|
||||
bool executeCommand(uint8_t opcode, uint32_t addr, uint32_t data, uint32_t* response_data);
|
||||
bool setError(const std::string& msg);
|
||||
|
||||
DigilentJtag jtag_;
|
||||
std::string last_error_;
|
||||
};
|
||||
104
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/prog.cpp
Normal file
104
cores/wb/jtag_wb_bridbe/tool/libjtag_wb_bridge/prog.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "argparse.hpp"
|
||||
#include "jtag_wb_bridge_client.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ArgParser parser(argc > 0 ? argv[0] : "test");
|
||||
parser.addString("file", "", "File to write", true, "f");
|
||||
parser.addFlag("verify", "Verify", "v");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
JtagWishboneBridge bridge;
|
||||
if (!bridge.open()) {
|
||||
std::printf("Could not open programmer: %s\n", bridge.lastError().c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!bridge.clearFlags()) {
|
||||
std::printf("Could not clear flags: %s\n", bridge.lastError().c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t ping_value = 0;
|
||||
if (!bridge.ping(&ping_value)) {
|
||||
std::printf("PING command failed: %s\n", bridge.lastError().c_str());
|
||||
return -1;
|
||||
}
|
||||
if (ping_value != 0xa5u) {
|
||||
std::printf("PING response was not right: %02x\n", ping_value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::string file = parser.getString("file");
|
||||
FILE* f = std::fopen(file.c_str(), "rb");
|
||||
if (!f) {
|
||||
std::printf("Could not open file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!bridge.setReset(true)) {
|
||||
std::printf("Could not assert reset: %s\n", bridge.lastError().c_str());
|
||||
std::fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int nr = 0;
|
||||
uint32_t addr = 0;
|
||||
do {
|
||||
uint32_t buf[32];
|
||||
nr = static_cast<int>(std::fread(buf, sizeof(uint32_t), 32, f));
|
||||
for (int i = 0; i < nr; ++i) {
|
||||
if (!bridge.write32(addr + static_cast<uint32_t>(i * 4), buf[i])) {
|
||||
std::printf("Write failed at %04x: %s\n",
|
||||
addr + static_cast<uint32_t>(i * 4),
|
||||
bridge.lastError().c_str());
|
||||
std::fclose(f);
|
||||
return -1;
|
||||
}
|
||||
std::printf(".");
|
||||
}
|
||||
std::printf("\n");
|
||||
|
||||
if (parser.getFlag("verify")) {
|
||||
for (int i = 0; i < nr; ++i) {
|
||||
uint32_t value = 0;
|
||||
if (!bridge.read32(addr + static_cast<uint32_t>(i * 4), &value)) {
|
||||
std::printf("Read failed at %04x: %s\n",
|
||||
addr + static_cast<uint32_t>(i * 4),
|
||||
bridge.lastError().c_str());
|
||||
std::fclose(f);
|
||||
return -1;
|
||||
}
|
||||
if (value != buf[i]) {
|
||||
std::printf(" -- Verify failed at %04x : %08x != %08x\n",
|
||||
addr + static_cast<uint32_t>(i * 4),
|
||||
value,
|
||||
buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addr += static_cast<uint32_t>(nr * 4);
|
||||
} while (nr > 0);
|
||||
|
||||
if (!bridge.setReset(false)) {
|
||||
std::printf("Could not deassert reset: %s\n", bridge.lastError().c_str());
|
||||
std::fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::fclose(f);
|
||||
return 0;
|
||||
}
|
||||
8
cores/wb/jtag_wb_bridbe/tool/test.py
Normal file
8
cores/wb/jtag_wb_bridbe/tool/test.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from libjtag_wb_bridge.jtag_bridge import JtagBridge
|
||||
|
||||
with JtagBridge() as bridge:
|
||||
bridge.open(port=0, chain=1)
|
||||
bridge.clear_flags()
|
||||
assert bridge.ping() == 0xA5
|
||||
|
||||
bridge.write32(0x0, 0xAA)
|
||||
69
cores/wb/wb_gpio/formal/formal_wb_gpio.v
Normal file
69
cores/wb/wb_gpio/formal/formal_wb_gpio.v
Normal file
@@ -0,0 +1,69 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module formal_wb_gpio #(
|
||||
parameter [31:0] address = 32'h00000000
|
||||
);
|
||||
(* gclk *) reg i_wb_clk;
|
||||
(* anyseq *) reg i_rst;
|
||||
(* anyseq *) reg i_wb_rst;
|
||||
(* anyseq *) reg [31:0] i_wb_adr;
|
||||
(* anyseq *) reg [31:0] i_wb_dat;
|
||||
(* anyseq *) reg [3:0] i_wb_sel;
|
||||
(* anyseq *) reg i_wb_we;
|
||||
(* anyseq *) reg i_wb_stb;
|
||||
(* anyseq *) reg [31:0] i_gpio;
|
||||
wire [31:0] o_wb_rdt;
|
||||
wire o_wb_ack;
|
||||
wire [31:0] o_gpio;
|
||||
wire i_wb_cyc;
|
||||
reg f_past_valid;
|
||||
|
||||
assign i_wb_cyc = i_wb_stb || o_wb_ack;
|
||||
|
||||
wb_gpio #(
|
||||
.address(address)
|
||||
) dut (
|
||||
.i_wb_clk(i_wb_clk),
|
||||
.i_wb_rst(i_wb_rst),
|
||||
.i_wb_adr(i_wb_adr),
|
||||
.i_wb_dat(i_wb_dat),
|
||||
.i_wb_sel(i_wb_sel),
|
||||
.i_wb_we(i_wb_we),
|
||||
.i_wb_stb(i_wb_stb),
|
||||
.i_gpio(i_gpio),
|
||||
.o_wb_rdt(o_wb_rdt),
|
||||
.o_wb_ack(o_wb_ack),
|
||||
.o_gpio(o_gpio)
|
||||
);
|
||||
|
||||
formal_wb_slave_checker wb_checker (
|
||||
.i_clk(i_wb_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_wb_rst(i_wb_rst),
|
||||
.i_wb_adr(i_wb_adr),
|
||||
.i_wb_dat(i_wb_dat),
|
||||
.i_wb_sel(i_wb_sel),
|
||||
.i_wb_we(i_wb_we),
|
||||
.i_wb_stb(i_wb_stb),
|
||||
.i_wb_cyc(i_wb_cyc),
|
||||
.o_wb_rdt(o_wb_rdt),
|
||||
.o_wb_ack(o_wb_ack)
|
||||
);
|
||||
|
||||
initial f_past_valid = 1'b0;
|
||||
|
||||
always @(posedge i_wb_clk) begin
|
||||
f_past_valid <= 1'b1;
|
||||
|
||||
// R1: reads return the sampled GPIO input on the following cycle
|
||||
if (f_past_valid && !$past(i_wb_rst) && !i_wb_rst && $past(i_wb_stb) && !$past(i_wb_we)) begin
|
||||
assert(o_wb_rdt == $past(i_gpio));
|
||||
end
|
||||
|
||||
// R2: reset clears the output register and read data register
|
||||
if (f_past_valid && $past(i_wb_rst)) begin
|
||||
assert(o_gpio == 32'h00000000);
|
||||
assert(o_wb_rdt == 32'h00000000);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
13
cores/wb/wb_gpio/formal/wb_gpio.sby
Normal file
13
cores/wb/wb_gpio/formal/wb_gpio.sby
Normal file
@@ -0,0 +1,13 @@
|
||||
[options]
|
||||
mode prove
|
||||
depth 8
|
||||
|
||||
[engines]
|
||||
smtbmc z3 parallel.enable=true parallel.threads.max=8
|
||||
|
||||
[script]
|
||||
{{"-formal"|gen_reads}}
|
||||
prep -top {{top_level}}
|
||||
|
||||
[files]
|
||||
{{files}}
|
||||
13
cores/wb/wb_gpio/formal/wb_gpio/config.sby
Normal file
13
cores/wb/wb_gpio/formal/wb_gpio/config.sby
Normal file
@@ -0,0 +1,13 @@
|
||||
[options]
|
||||
mode prove
|
||||
depth 8
|
||||
|
||||
[engines]
|
||||
smtbmc z3 parallel.enable=true parallel.threads.max=8
|
||||
|
||||
[script]
|
||||
{{"-formal"|gen_reads}}
|
||||
prep -top {{top_level}}
|
||||
|
||||
[files]
|
||||
{{files}}
|
||||
2
cores/wb/wb_gpio/formal/wb_gpio/logfile.txt
Normal file
2
cores/wb/wb_gpio/formal/wb_gpio/logfile.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
SBY 17:32:33 [cores/wb/wb_gpio/formal/wb_gpio] Removing directory '/data/joppe/projects/fusesoc_test/cores/wb/wb_gpio/formal/wb_gpio'.
|
||||
SBY 17:32:33 [cores/wb/wb_gpio/formal/wb_gpio] Copy '/data/joppe/projects/fusesoc_test/{{files}}' to '/data/joppe/projects/fusesoc_test/cores/wb/wb_gpio/formal/wb_gpio/src/{{files}}'.
|
||||
56
cores/wb/wb_gpio/rtl/wb_gpio.v
Normal file
56
cores/wb/wb_gpio/rtl/wb_gpio.v
Normal file
@@ -0,0 +1,56 @@
|
||||
module wb_gpio #(
|
||||
parameter address = 32'h00000000
|
||||
)(
|
||||
input wire i_wb_clk,
|
||||
input wire i_wb_rst, // optional; tie low if unused
|
||||
input wire [31:0] i_wb_adr, // optional; can ignore for single-reg
|
||||
input wire [31:0] i_wb_dat,
|
||||
input wire [3:0] i_wb_sel,
|
||||
input wire i_wb_we,
|
||||
input wire i_wb_stb,
|
||||
input wire [31:0] i_gpio,
|
||||
|
||||
output reg [31:0] o_wb_rdt,
|
||||
output reg o_wb_ack,
|
||||
output reg [31:0] o_gpio
|
||||
);
|
||||
|
||||
initial o_gpio <= 32'h00000000;
|
||||
initial o_wb_rdt <= 32'h00000000;
|
||||
|
||||
wire addr_check;
|
||||
assign addr_check = (i_wb_adr == address);
|
||||
|
||||
// One-cycle ACK pulse per request (works even if stb stays high)
|
||||
initial o_wb_ack <= 1'b0;
|
||||
always @(posedge i_wb_clk) begin
|
||||
if (i_wb_rst) begin
|
||||
o_wb_ack <= 1'b0;
|
||||
end else begin
|
||||
o_wb_ack <= i_wb_stb & ~o_wb_ack; // pulse while stb asserted
|
||||
end
|
||||
end
|
||||
|
||||
// Read data (combinational or registered; registered here)
|
||||
always @(posedge i_wb_clk) begin
|
||||
if (i_wb_rst) begin
|
||||
o_wb_rdt <= 32'h0;
|
||||
end else if (i_wb_stb && !i_wb_we) begin
|
||||
o_wb_rdt <= i_gpio;
|
||||
end
|
||||
end
|
||||
|
||||
// Write latch (update on the acknowledged cycle)
|
||||
always @(posedge i_wb_clk) begin
|
||||
if (i_wb_rst) begin
|
||||
o_gpio <= 32'h0;
|
||||
end else if (i_wb_stb && i_wb_we && addr_check && (i_wb_stb & ~o_wb_ack)) begin
|
||||
// Apply byte enables (so sb works if the master uses sel)
|
||||
if (i_wb_sel[0]) o_gpio[7:0] <= i_wb_dat[7:0];
|
||||
if (i_wb_sel[1]) o_gpio[15:8] <= i_wb_dat[15:8];
|
||||
if (i_wb_sel[2]) o_gpio[23:16] <= i_wb_dat[23:16];
|
||||
if (i_wb_sel[3]) o_gpio[31:24] <= i_wb_dat[31:24];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
43
cores/wb/wb_gpio/wb_gpio.core
Normal file
43
cores/wb/wb_gpio/wb_gpio.core
Normal file
@@ -0,0 +1,43 @@
|
||||
CAPI=2:
|
||||
|
||||
name: joppeb:wb:wb_gpio:1.0
|
||||
description: Wishbone GPIO peripheral
|
||||
|
||||
filesets:
|
||||
rtl:
|
||||
files:
|
||||
- rtl/wb_gpio.v
|
||||
file_type: verilogSource
|
||||
formal_rtl:
|
||||
depend:
|
||||
- joppeb:wb:formal_checker
|
||||
files:
|
||||
- formal/formal_wb_gpio.v
|
||||
file_type: verilogSource
|
||||
formal_cfg:
|
||||
files:
|
||||
- formal/wb_gpio.sby
|
||||
file_type: sbyConfigTemplate
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- rtl
|
||||
toplevel: wb_gpio
|
||||
parameters:
|
||||
- address
|
||||
formal:
|
||||
default_tool: symbiyosys
|
||||
filesets:
|
||||
- rtl
|
||||
- formal_rtl
|
||||
- formal_cfg
|
||||
toplevel: formal_wb_gpio
|
||||
parameters:
|
||||
- address
|
||||
|
||||
parameters:
|
||||
address:
|
||||
datatype: int
|
||||
description: Wishbone address matched by this peripheral
|
||||
paramtype: vlogparam
|
||||
46
cores/wb/wb_mem32/formal/formal_wb_mem32.v
Normal file
46
cores/wb/wb_mem32/formal/formal_wb_mem32.v
Normal file
@@ -0,0 +1,46 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module formal_wb_mem32;
|
||||
(* gclk *) reg i_clk;
|
||||
(* anyseq *) reg i_rst;
|
||||
(* anyseq *) reg i_wb_rst;
|
||||
(* anyseq *) reg [31:0] i_wb_adr;
|
||||
(* anyseq *) reg [31:0] i_wb_dat;
|
||||
(* anyseq *) reg [3:0] i_wb_sel;
|
||||
(* anyseq *) reg i_wb_we;
|
||||
(* anyseq *) reg i_wb_stb;
|
||||
(* anyseq *) reg i_wb_cyc;
|
||||
wire [31:0] o_wb_rdt;
|
||||
wire o_wb_ack;
|
||||
|
||||
wb_mem32 #(
|
||||
.memsize(16),
|
||||
.sim(1)
|
||||
) dut (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_wb_rst(i_wb_rst),
|
||||
.i_wb_adr(i_wb_adr),
|
||||
.i_wb_dat(i_wb_dat),
|
||||
.i_wb_sel(i_wb_sel),
|
||||
.i_wb_we(i_wb_we),
|
||||
.i_wb_stb(i_wb_stb),
|
||||
.i_wb_cyc(i_wb_cyc),
|
||||
.o_wb_rdt(o_wb_rdt),
|
||||
.o_wb_ack(o_wb_ack)
|
||||
);
|
||||
|
||||
formal_wb_slave_checker wb_checker (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_wb_rst(i_wb_rst),
|
||||
.i_wb_adr(i_wb_adr),
|
||||
.i_wb_dat(i_wb_dat),
|
||||
.i_wb_sel(i_wb_sel),
|
||||
.i_wb_we(i_wb_we),
|
||||
.i_wb_stb(i_wb_stb),
|
||||
.i_wb_cyc(i_wb_cyc),
|
||||
.o_wb_rdt(o_wb_rdt),
|
||||
.o_wb_ack(o_wb_ack)
|
||||
);
|
||||
endmodule
|
||||
15
cores/wb/wb_mem32/formal/wb_mem32.sby
Normal file
15
cores/wb/wb_mem32/formal/wb_mem32.sby
Normal file
@@ -0,0 +1,15 @@
|
||||
[options]
|
||||
mode prove
|
||||
depth 8
|
||||
|
||||
[engines]
|
||||
smtbmc z3 parallel.enable=true parallel.threads.max=8
|
||||
|
||||
[script]
|
||||
read -formal clog2.vh
|
||||
{{"-formal"|gen_reads}}
|
||||
prep -top {{top_level}}
|
||||
|
||||
[files]
|
||||
src/joppeb_util_clog2_1.0/clog2.vh
|
||||
{{files}}
|
||||
66
cores/wb/wb_mem32/rtl/wb_mem32.v
Normal file
66
cores/wb/wb_mem32/rtl/wb_mem32.v
Normal file
@@ -0,0 +1,66 @@
|
||||
`timescale 1ns/1ps
|
||||
`include "clog2.vh"
|
||||
|
||||
module wb_mem32 #(
|
||||
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,
|
||||
input wire i_wb_cyc,
|
||||
output wire [31:0] o_wb_rdt,
|
||||
output wire o_wb_ack
|
||||
);
|
||||
localparam integer mem_depth = memsize/4;
|
||||
localparam integer mem_aw = (mem_depth <= 1) ? 1 : `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 & i_wb_cyc & ~wb_ack_r;
|
||||
|
||||
if (i_wb_stb & i_wb_cyc & ~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
|
||||
178
cores/wb/wb_mem32/tb/tb_wb_mem32.v
Normal file
178
cores/wb/wb_mem32/tb/tb_wb_mem32.v
Normal file
@@ -0,0 +1,178 @@
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_wb_mem32;
|
||||
reg i_clk;
|
||||
reg i_rst;
|
||||
reg i_wb_rst;
|
||||
reg [31:0] i_wb_adr;
|
||||
reg [31:0] i_wb_dat;
|
||||
reg [3:0] i_wb_sel;
|
||||
reg i_wb_we;
|
||||
reg i_wb_stb;
|
||||
reg i_wb_cyc;
|
||||
wire [31:0] o_wb_rdt;
|
||||
wire o_wb_ack;
|
||||
|
||||
reg [31:0] read_data;
|
||||
|
||||
wb_mem32 #(
|
||||
.memsize(64),
|
||||
.sim(1)
|
||||
) dut (
|
||||
.i_clk(i_clk),
|
||||
.i_rst(i_rst),
|
||||
.i_wb_rst(i_wb_rst),
|
||||
.i_wb_adr(i_wb_adr),
|
||||
.i_wb_dat(i_wb_dat),
|
||||
.i_wb_sel(i_wb_sel),
|
||||
.i_wb_we(i_wb_we),
|
||||
.i_wb_stb(i_wb_stb),
|
||||
.i_wb_cyc(i_wb_cyc),
|
||||
.o_wb_rdt(o_wb_rdt),
|
||||
.o_wb_ack(o_wb_ack)
|
||||
);
|
||||
|
||||
initial i_clk = 1'b0;
|
||||
always #5 i_clk = ~i_clk;
|
||||
|
||||
task automatic wb_write;
|
||||
input [31:0] addr;
|
||||
input [31:0] data;
|
||||
input [3:0] sel;
|
||||
begin
|
||||
@(negedge i_clk);
|
||||
i_wb_adr <= addr;
|
||||
i_wb_dat <= data;
|
||||
i_wb_sel <= sel;
|
||||
i_wb_we <= 1'b1;
|
||||
i_wb_stb <= 1'b1;
|
||||
i_wb_cyc <= 1'b1;
|
||||
|
||||
@(posedge i_clk);
|
||||
#1;
|
||||
if (!o_wb_ack) begin
|
||||
$display("ERROR: write ack missing at time %0t", $time);
|
||||
$finish;
|
||||
end
|
||||
|
||||
@(posedge i_clk);
|
||||
i_wb_stb <= 1'b0;
|
||||
i_wb_cyc <= 1'b0;
|
||||
i_wb_we <= 1'b0;
|
||||
i_wb_sel <= 4'b0000;
|
||||
i_wb_dat <= 32'h0;
|
||||
|
||||
@(posedge i_clk);
|
||||
#1;
|
||||
if (o_wb_ack) begin
|
||||
$display("ERROR: write ack did not clear at time %0t", $time);
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic wb_read;
|
||||
input [31:0] addr;
|
||||
output [31:0] data;
|
||||
begin
|
||||
@(negedge i_clk);
|
||||
i_wb_adr <= addr;
|
||||
i_wb_dat <= 32'h0;
|
||||
i_wb_sel <= 4'b1111;
|
||||
i_wb_we <= 1'b0;
|
||||
i_wb_stb <= 1'b1;
|
||||
i_wb_cyc <= 1'b1;
|
||||
|
||||
@(posedge i_clk);
|
||||
#1;
|
||||
if (!o_wb_ack) begin
|
||||
$display("ERROR: read ack missing at time %0t", $time);
|
||||
$finish;
|
||||
end
|
||||
data = o_wb_rdt;
|
||||
|
||||
@(posedge i_clk);
|
||||
i_wb_stb <= 1'b0;
|
||||
i_wb_cyc <= 1'b0;
|
||||
i_wb_sel <= 4'b0000;
|
||||
|
||||
@(posedge i_clk);
|
||||
#1;
|
||||
if (o_wb_ack) begin
|
||||
$display("ERROR: read ack did not clear at time %0t", $time);
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
$dumpfile("wb_mem32.vcd");
|
||||
$dumpvars(0, tb_wb_mem32);
|
||||
|
||||
i_rst = 1'b1;
|
||||
i_wb_rst = 1'b0;
|
||||
i_wb_adr = 32'h0;
|
||||
i_wb_dat = 32'h0;
|
||||
i_wb_sel = 4'b0000;
|
||||
i_wb_we = 1'b0;
|
||||
i_wb_stb = 1'b0;
|
||||
i_wb_cyc = 1'b0;
|
||||
|
||||
repeat (2) @(posedge i_clk);
|
||||
i_rst = 1'b0;
|
||||
|
||||
@(negedge i_clk);
|
||||
i_wb_adr <= 32'h0000_0000;
|
||||
i_wb_sel <= 4'b1111;
|
||||
i_wb_stb <= 1'b1;
|
||||
i_wb_cyc <= 1'b0;
|
||||
|
||||
@(posedge i_clk);
|
||||
#1;
|
||||
if (o_wb_ack) begin
|
||||
$display("ERROR: ack asserted without cyc at time %0t", $time);
|
||||
$finish;
|
||||
end
|
||||
|
||||
@(negedge i_clk);
|
||||
i_wb_stb <= 1'b0;
|
||||
i_wb_sel <= 4'b0000;
|
||||
|
||||
wb_read(32'h0000_0000, read_data);
|
||||
if (read_data !== 32'h0000_0000) begin
|
||||
$display("ERROR: reset contents mismatch, got %08x", read_data);
|
||||
$finish;
|
||||
end
|
||||
|
||||
wb_write(32'h0000_0000, 32'hA1B2_C3D4, 4'b1111);
|
||||
wb_read(32'h0000_0000, read_data);
|
||||
if (read_data !== 32'hA1B2_C3D4) begin
|
||||
$display("ERROR: full-word write mismatch, got %08x", read_data);
|
||||
$finish;
|
||||
end
|
||||
|
||||
wb_write(32'h0000_0000, 32'h5566_7788, 4'b0101);
|
||||
wb_read(32'h0000_0000, read_data);
|
||||
if (read_data !== 32'hA166_C388) begin
|
||||
$display("ERROR: byte-enable write mismatch, got %08x", read_data);
|
||||
$finish;
|
||||
end
|
||||
|
||||
wb_write(32'h0000_0004, 32'hDEAD_BEEF, 4'b1111);
|
||||
wb_read(32'h0000_0004, read_data);
|
||||
if (read_data !== 32'hDEAD_BEEF) begin
|
||||
$display("ERROR: second word mismatch, got %08x", read_data);
|
||||
$finish;
|
||||
end
|
||||
|
||||
wb_read(32'h0000_0000, read_data);
|
||||
if (read_data !== 32'hA166_C388) begin
|
||||
$display("ERROR: first word changed unexpectedly, got %08x", read_data);
|
||||
$finish;
|
||||
end
|
||||
|
||||
$display("PASS: wb_mem32 testbench completed successfully");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
63
cores/wb/wb_mem32/wb_mem32.core
Normal file
63
cores/wb/wb_mem32/wb_mem32.core
Normal file
@@ -0,0 +1,63 @@
|
||||
CAPI=2:
|
||||
|
||||
name: joppeb:wb:wb_mem32:1.0
|
||||
description: Wishbone classic block ram
|
||||
|
||||
filesets:
|
||||
rtl:
|
||||
depend:
|
||||
- joppeb:util:clog2
|
||||
files:
|
||||
- rtl/wb_mem32.v
|
||||
file_type: verilogSource
|
||||
tb:
|
||||
files:
|
||||
- tb/tb_wb_mem32.v
|
||||
file_type: verilogSource
|
||||
formal_rtl:
|
||||
depend:
|
||||
- joppeb:wb:formal_checker
|
||||
files:
|
||||
- formal/formal_wb_mem32.v
|
||||
file_type: verilogSource
|
||||
formal_cfg:
|
||||
files:
|
||||
- formal/wb_mem32.sby
|
||||
file_type: sbyConfigTemplate
|
||||
|
||||
targets:
|
||||
default:
|
||||
filesets:
|
||||
- rtl
|
||||
toplevel: wb_mem32
|
||||
parameters:
|
||||
- memfile
|
||||
- memsize
|
||||
- sim
|
||||
sim:
|
||||
default_tool: icarus
|
||||
filesets:
|
||||
- rtl
|
||||
- tb
|
||||
toplevel: tb_wb_mem32
|
||||
formal:
|
||||
default_tool: symbiyosys
|
||||
filesets:
|
||||
- rtl
|
||||
- formal_rtl
|
||||
- formal_cfg
|
||||
toplevel: formal_wb_mem32
|
||||
|
||||
parameters:
|
||||
memfile:
|
||||
datatype: str
|
||||
description: Data to fill the mem
|
||||
paramtype: vlogparam
|
||||
memsize:
|
||||
datatype: int
|
||||
description: Size of memory in bytes, should be a multiple of 32bit
|
||||
paramtype: vlogparam
|
||||
sim:
|
||||
datatype: int
|
||||
description: Simulation version, fills rest memory with 0
|
||||
paramtype: vlogparam
|
||||
Reference in New Issue
Block a user