Add draft UART DTM

This commit is contained in:
Luke Wren 2021-07-08 17:57:46 +01:00
parent 6a38fc33a6
commit 3312ea7022
9 changed files with 3200 additions and 646 deletions

View File

@ -12,3 +12,5 @@ include::sections/introduction.adoc[]
include::sections/instruction_timings.adoc[]
include::sections/csr.adoc[]
include::sections/debug.adoc[]

File diff suppressed because it is too large Load Diff

52
doc/sections/debug.adoc Normal file
View File

@ -0,0 +1,52 @@
== Debug
Currently the plan is for Hazard3, with its associated debug module (DM), to support the following:
* Run/halt/reset control as required
* Abstract GPR access as required
* Program buffer: 2 words plus `impebreak`
* Automatic program buffer execution triggered by abstract GPR access (`abstractauto`)
* Some minimum useful trigger unit -- likely just breakpoints, no watchpoints
The core itself will implement the following, enabling the DM to provide a compliant debug interface:
* Debug mode CSRs
* Ability to enter debug mode with correct update of `dpc` etc
** Synchronously via exception, `ebreak` or trigger match
** Asynchronously via external halt request
* Ability to exit debug mode to M mode
* Address query/match interface for external trigger unit
* Ability to inject words into the instruction prefetch queue when the processor is halted
* Ability to suppress exception entry when executing instructions in debug mode, and provide an external signal to indicate the exception took place
* A read/write data bus which allows the DM to intercept core CSR accesses
The DM implements abstract GPR access by injecting a dummy CSR access instruction, and manipulating the CSR port to get data in/out of the core. A `csrr` is used to write to a core register, and a `csrw` to read from a core register. By injecting a `csrrw`, the DM can _swap_ a GPR with one of its own internal registers, though this is not exposed through the abstract GPR access command.
The debugger implements memory and CSR access using the Program Buffer, which uses the same instruction injection interface used by the DM to implement abstract GPR access. The `abstractauto` feature allows the DM to execute the program buffer automatically following every abstract GPR access, which can be used for e.g. autoincrementing read/write memory bursts.
=== UART DTM
Hazard3 defines a minimal UART Debug Transport Module, which allows the Debug Module to be accessed via a standard 8n1 asynchronous serial port. The UART DTM is always accessed by the host using a two-wire serial interface (TXD RXD) running at 1 Mbaud. The interface between the DTM and DM is an AMBA 3 APB port with a 32-bit data bus and 8-bit address bus.
This is not intended for production systems:
* Debug hardware should not expect a frequency reference for a UART to be present
* The UART DTM does not implement any flow control or error detection/correction
However, it suffices for bringup and playing around on FPGA boards. The host sends a 6-byte packet:
* Command:
** `0x00` nop, ignored, next command can follow immediately (no address or data bytes, no response)
** `0x01` read
** `0x02` write
** `0xa5` return to idle (no address or data bytes, no response)
* One address byte
* 4 data bytes (write) or 4 zero-padding bytes (read)
The 6-byte framing can be recovered at any time by writing 6 zero-bytes, which will be interpreted as between 1 and 6 nops depending on current DTM state.
The DTM always responds with four data bytes. For a read command this will be the data read from the given address. For a write command this will echo back the write data.
This interface assumes the actual data transfer takes very little time compared with the UART access (typically less than one baud period). Because the host-to-DTM bandwidth is always greater than the DTM-to-host bandwidth, the host can queue up batches of commands in its transmit buffer, and this should never overrun the DTM's response channel. So, the 1 Mbaud 8n1 UART link provides 67 kB/s of half-duplex data bandwidth between host and DM, which is enough to get your system off the ground.
Initially after power-on the DTM is in the idle state, and will ignore any commands. The host sends the magic sequence `'S', 'U', 'P', '?'` (`0x53, 0x55, 0x50, 0x3f`) to wake the DTM, and then attempts to access a read-only DM register such as `dmstatus` to make sure the link is up. The DTM can be returned to the idle at any time using the `0xa5` return-to-idle command.

View File

@ -0,0 +1,192 @@
/**********************************************************************
* DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE *
* Version 3, April 2008 *
* *
* Copyright (C) 2021 Luke Wren *
* *
* Everyone is permitted to copy and distribute verbatim or modified *
* copies of this license document and accompanying software, and *
* changing either is allowed. *
* *
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION *
* *
* 0. You just DO WHAT THE FUCK YOU WANT TO. *
* 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK. *
* *
*********************************************************************/
// APB-to-APB asynchronous bridge for connecting DTM to DM, in case DTM is in
// a different clock domain (e.g. running directly from crystal to get a
// fixed baud reference)
//
// Note this module depends on the hazard3_sync_1bit module (a flop-chain
// synchroniser) which should be reimplemented for your FPGA/process.
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *)
`endif
module hazard3_apb_async_bridge #(
parameter W_ADDR = 8,
parameter W_DATA = 32
) (
// Resets assumed to be synchronised externally
input wire clk_src,
input wire rst_n_src,
input wire clk_dst,
input wire rst_n_dst,
// APB port from Transport Module
input wire src_psel,
input wire src_penable,
input wire src_pwrite,
input wire [W_ADDR-1:0] src_paddr,
input wire [W_DATA-1:0] src_pwdata,
output wire [W_DATA-1:0] src_prdata,
output wire src_pready,
output wire src_pslverr,
// APB port to Debug Module
output wire dst_psel,
output wire dst_penable,
output wire dst_pwrite,
output wire [W_ADDR-1:0] dst_paddr,
output wire [W_DATA-1:0] dst_pwdata,
input wire [W_DATA-1:0] dst_prdata,
input wire dst_pready,
input wire dst_pslverr
);
localparam N_SYNC_STAGES = 3;
// ----------------------------------------------------------------------------
// Clock-crossing registers
// We're using a modified req/ack handshake:
//
// - Initially both req and ack are low
// - src asserts req high
// - dst responds with ack high and begins transfer
// - src deasserts req once it sees ack low
// - dst deasserts ack once:
// - transfer is complete *and*
// - dst sees req deasserted
// - Once src sees ack low, a new transfer can begin.
//
// A NRZI toggle handshake might be more appropriate, but can cause spurious
// bus accesses when only one side of the link is reset.
`HAZARD3_REG_KEEP_ATTRIBUTE reg src_req;
wire dst_req;
`HAZARD3_REG_KEEP_ATTRIBUTE reg dst_ack;
wire src_ack;
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] src_paddr_pwdata_pwrite;
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_ADDR + W_DATA + 1 -1:0] dst_paddr_pwdata_pwrite;
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] dst_prdata_pslverr;
`HAZARD3_REG_KEEP_ATTRIBUTE reg [W_DATA + 1 -1:0] src_prdata_pslverr;
hazard3_sync_1bit #(
.N_STAGES (N_SYNC_STAGES)
) sync_req (
.clk (clk_dst),
.rst_n (rst_n_dst),
.i (src_req),
.o (dst_req)
);
hazard3_sync_1bit #(
.N_STAGES (N_SYNC_STAGES)
) sync_ack (
.clk (clk_src),
.rst_n (rst_n_src),
.i (dst_ack),
.o (src_ack)
);
// ----------------------------------------------------------------------------
// src state machine
reg src_waiting_for_downstream;
reg src_pready_r;
always @ (posedge clk_src or negedge rst_n_src) begin
if (!rst_n_src) begin
src_req <= 1'b0;
src_waiting_for_downstream <= 1'b0;
src_paddr_pwdata_pwrite <= {W_ADDR + W_DATA + 1{1'b0}};
src_prdata_pslverr <= {W_DATA + 1{1'b0}};
src_pready_r <= 1'b1;
end else if (src_waiting_for_downstream) begin
if (src_req && src_ack) begin
// Request was acknowledged, so deassert.
src_req <= 1'b0;
end else if (!(src_req || src_ack)) begin
// Downstream transfer has finished, data is valid.
src_pready_r <= 1'b1;
src_waiting_for_downstream <= 1'b0;
// Note this assignment is cross-domain (but data has been stable
// for duration of ack synchronisation delay):
src_prdata_pslverr <= dst_prdata_pslverr;
end
end else begin
// paddr, pwdata and pwrite are all valid during the setup phase, and
// APB defines the setup phase to always last one cycle and proceed
// to access phase. So, we can ignore penable, and pready is ignored.
if (src_psel) begin
src_pready_r <= 1'b0;
src_req <= 1'b1;
src_paddr_pwdata_pwrite <= {src_paddr, src_pwdata, src_pwrite};
src_waiting_for_downstream <= 1'b1;
end
end
end
assign {src_prdata, src_pslverr} = src_prdata_pslverr;
assign src_pready = src_pready_r;
// ----------------------------------------------------------------------------
// dst state machine
wire dst_bus_finish = dst_penable && dst_pready;
always @ (posedge clk_dst or negedge rst_n_dst) begin
if (!rst_n_dst) begin
dst_ack <= 1'b0;
end else if (dst_req) begin
dst_ack <= 1'b1;
end else if (!dst_req && dst_penable && dst_pready) begin
dst_ack <= 1'b0;
end
end
reg dst_psel_r;
reg dst_penable_r;
always @ (posedge clk_dst or negedge rst_n_dst) begin
if (!rst_n_dst) begin
dst_psel_r <= 1'b0;
dst_penable_r <= 1'b0;
dst_prdata_pslverr <= {W_DATA + 1{1'b0}};
end else if (dst_req && !dst_ack) begin
dst_psel_r <= 1'b1;
// Note this assignment is cross-domain. The src register has been
// stable for the duration of the req sync delay.
dst_paddr_pwdata_pwrite <= src_paddr_pwdata_pwrite;
end else if (dst_psel_r && !dst_penable_r) begin
dst_penable_r <= 1'b1;
end else if (dst_bus_finish) begin
dst_psel_r <= 1'b0;
dst_penable_r <= 1'b0;
dst_prdata_pslverr <= {dst_prdata, dst_pslverr};
end
end
assign dst_psel = dst_psel_r;
assign dst_penable = dst_penable_r;
assign {dst_paddr, dst_pwdata, dst_pwrite} = dst_paddr_pwdata_pwrite;
endmodule

View File

@ -0,0 +1,45 @@
/**********************************************************************
* DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE *
* Version 3, April 2008 *
* *
* Copyright (C) 2021 Luke Wren *
* *
* Everyone is permitted to copy and distribute verbatim or modified *
* copies of this license document and accompanying software, and *
* changing either is allowed. *
* *
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION *
* *
* 0. You just DO WHAT THE FUCK YOU WANT TO. *
* 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK. *
* *
*********************************************************************/
// A 2FF synchronizer to mitigate metastabilities. This is a baseline
// implementation -- you should replace it with cells specific to your
// FPGA/process
`ifndef HAZARD3_REG_KEEP_ATTRIBUTE
`define HAZARD3_REG_KEEP_ATTRIBUTE (* keep = 1'b1 *)
`endif
module hazard3_sync_1bit #(
parameter N_STAGES = 2 // Should be >=2
) (
input wire clk,
input wire rst_n,
input wire i,
output wire o
);
`HAZARD3_REG_KEEP_ATTRIBUTE reg [N_STAGES-1:0] sync_flops;
always @ (posedge clk or negedge rst_n)
if (!rst_n)
sync_flops <= {N_STAGES{1'b0}};
else
sync_flops <= {sync_flops[N_STAGES-2:0], i};
assign o = sync_flops[N_STAGES-1];
endmodule

View File

@ -0,0 +1,6 @@
file hazard3_uart_dtm.v
file hazard3_uart_dtm_fifo.v
file hazard3_sync_1bit.v
# Optional clock crossing for DTM-to-DM bus:
file hazard3_apb_async_bridge.v

View File

@ -0,0 +1,317 @@
/**********************************************************************
* DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE *
* Version 3, April 2008 *
* *
* Copyright (C) 2021 Luke Wren *
* *
* Everyone is permitted to copy and distribute verbatim or modified *
* copies of this license document and accompanying software, and *
* changing either is allowed. *
* *
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION *
* *
* 0. You just DO WHAT THE FUCK YOU WANT TO. *
* 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK. *
* *
*********************************************************************/
// UART Debug Transport Module: connect an external two-wire 1 Mbaud UART
// interface to an APB Debug Module port.
//
// This is not suitable for production systems (it's a UART...) but is a
// simple way to get your FPGA board up and running.
module hazard3_uart_dtm #(
// Expected to run at 1 Mbaud from some fixed reference frequency.
parameter BAUD_CLKDIV = 12,
parameter W_BAUDCTR = $clog2(BAUD_CLKDIV) // do not modify
) (
input wire clk,
input wire rst_n,
// External UART interface
input wire rx,
output wire tx,
// APB port to Debug Module
output wire psel,
output wire penable,
output wire pwrite,
output wire [7:0] paddr,
output wire [31:0] pwdata,
input wire [31:0] prdata,
input wire pready,
input wire pslverr
);
// ----------------------------------------------------------------------------
// Serial interface
wire [7:0] tx_wdata;
wire tx_wvld;
wire tx_wrdy;
wire [7:0] tx_rdata;
wire tx_rvld;
wire tx_rrdy = 1'b1;
wire [7:0] rx_wdata;
wire rx_wvld;
wire rx_wrdy;
wire [7:0] rx_rdata;
wire rx_rvld;
wire rx_rrdy;
hazard3_uart_dtm_fifo #(
.WIDTH(8),
.LOG_DEPTH(2)
) tx_fifo (
.clk (clk),
.rst_n (rst_n),
.wdata (tx_wdata),
.wvld (tx_wvld),
.wrdy (tx_wrdy),
.rdata (tx_rdata),
.rvld (tx_rvld),
.rrdy (tx_rrdy)
);
hazard3_uart_dtm_fifo #(
.WIDTH(8),
.LOG_DEPTH(2)
) rx_fifo (
.clk (clk),
.rst_n (rst_n),
.wdata (rx_wdata),
.wvld (rx_wvld),
.wrdy (rx_wrdy),
.rdata (rx_rdata),
.rvld (rx_rvld),
.rrdy (rx_rrdy)
);
reg [W_BAUDCTR-1:0] tx_baudctr;
reg [9:0] tx_shiftreg;
reg [3:0] tx_shiftctr;
assign tx_rrdy = ~|tx_shiftctr;
assign tx = tx_shiftreg[0];
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
tx_baudctr <= {W_BAUDCTR{1'b0}};
tx_shiftreg <= 10'h3ff;
tx_shiftctr <= 4'd0;
end else if (tx_rvld && tx_rrdy) begin
tx_baudctr <= BAUD_CLKDIV - 1;
tx_shiftreg <= {1'b1, tx_rdata, 1'b0};
tx_shiftctr <= 4'd10;
end else if (|tx_baudctr) begin
tx_baudctr <= tx_baudctr - 1'b1;
end else if (|tx_shiftctr) begin
tx_baudctr <= BAUD_CLKDIV - 1;
tx_shiftreg <= {1'b1, tx_shiftreg[9:1]};
tx_shiftctr <= tx_shiftctr - 1'b1;
end
end
wire rx_sync;
hazard3_sync_1bit #(
.N_STAGES (2)
) sync_req (
.clk (clk),
.rst_n (rst_n),
.i (rx),
.o (rx_sync)
);
reg [W_BAUDCTR-1:0] rx_baudctr;
reg [7:0] rx_shiftreg;
reg [3:0] rx_shiftctr;
// Only push if the frame ends with a valid stop bit:
assign rx_wvld = ~|rx_baudctr && rx_shiftctr == 4'd1 && rx_sync;
assign rx_wdata = rx_shiftreg;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
rx_baudctr <= {W_BAUDCTR{1'b0}};
rx_shiftreg <= 8'h00;
rx_shiftctr <= 4'd0;
end else if (~|rx_shiftctr && ~|rx_baudctr && !rx_sync) begin
rx_shiftctr <= 4'd10;
// Start with half-period to get sampling alignment
rx_baudctr <= (BAUD_CLKDIV - 1) / 2;
end else if (|rx_baudctr) begin
rx_baudctr <= rx_baudctr - 1'b1;
end else if (|rx_shiftctr) begin
rx_baudctr <= BAUD_CLKDIV - 1;
rx_shiftctr <= rx_shiftctr - 1'b1;
if (rx_shiftctr != 4'd1 && rx_shiftctr != 4'd10)
rx_shiftreg <= {rx_sync, rx_shiftreg[7:1]};
end
end
// ----------------------------------------------------------------------------
// Command state machine
localparam W_STATE = 5;
localparam [W_STATE-1:0] S_IDLE0 = 5'd0;
localparam [W_STATE-1:0] S_IDLE1 = 5'd1;
localparam [W_STATE-1:0] S_IDLE2 = 5'd2;
localparam [W_STATE-1:0] S_IDLE3 = 5'd3;
localparam [W_STATE-1:0] S_CMD = 5'd4;
localparam [W_STATE-1:0] S_WADDR = 5'd5;
localparam [W_STATE-1:0] S_WDATA0 = 5'd6;
localparam [W_STATE-1:0] S_WDATA1 = 5'd7;
localparam [W_STATE-1:0] S_WDATA2 = 5'd8;
localparam [W_STATE-1:0] S_WDATA3 = 5'd9;
localparam [W_STATE-1:0] S_WSETUP = 5'd10;
localparam [W_STATE-1:0] S_WACCESS = 5'd11;
localparam [W_STATE-1:0] S_RADDR = 5'd12;
localparam [W_STATE-1:0] S_RSETUP = 5'd13;
localparam [W_STATE-1:0] S_RACCESS = 5'd14;
localparam [W_STATE-1:0] S_RDATA0 = 5'd15;
localparam [W_STATE-1:0] S_RDATA1 = 5'd16;
localparam [W_STATE-1:0] S_RDATA2 = 5'd17;
localparam [W_STATE-1:0] S_RDATA3 = 5'd18;
localparam CMD_NOP = 8'h00;
localparam CMD_READ = 8'h01;
localparam CMD_WRITE = 8'h02;
localparam CMD_RETURN_TO_IDLE = 8'ha5;
reg [W_STATE-1:0] state;
reg [7:0] dm_addr;
reg [31:0] dm_data;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_IDLE0;
dm_addr <= 8'h0;
dm_data <= 32'h0;
end else case (state)
S_IDLE0: if (rx_rvld) state <= rx_rdata == "S" ? S_IDLE1 : S_IDLE0;
S_IDLE1: if (rx_rvld) state <= rx_rdata == "U" ? S_IDLE2 : S_IDLE0;
S_IDLE2: if (rx_rvld) state <= rx_rdata == "P" ? S_IDLE3 : S_IDLE0;
S_IDLE3: if (rx_rvld) state <= rx_rdata == "?" ? S_CMD : S_IDLE0;
S_CMD: if (rx_rvld) begin
if (rx_rdata == CMD_READ)
state <= S_RADDR;
else if (rx_rdata == CMD_WRITE)
state <= S_WADDR;
else if (rx_rdata == CMD_RETURN_TO_IDLE)
state <= S_IDLE0;
// NOP or invalid leave DTM in command state.
end
S_WADDR: if (rx_rvld) begin
state <= S_WDATA0;
dm_addr <= rx_rdata;
end
S_WDATA0: if (rx_rvld) begin
state <= S_WDATA1;
dm_data <= {rx_rdata, dm_data[31:8]};
end
S_WDATA1: if (rx_rvld) begin
state <= S_WDATA2;
dm_data <= {rx_rdata, dm_data[31:8]};
end
S_WDATA2: if (rx_rvld) begin
state <= S_WDATA3;
dm_data <= {rx_rdata, dm_data[31:8]};
end
S_WDATA3: if (rx_rvld) begin
state <= S_WSETUP;
dm_data <= {rx_rdata, dm_data[31:8]};
end
S_WSETUP: state <= S_WACCESS;
S_WACCESS: if (pready) state <= S_CMD;
S_RADDR: if (rx_rvld) begin
state <= S_RSETUP;
dm_addr <= rx_rdata;
end
S_RSETUP: state <= S_RACCESS;
S_RACCESS: if (pready) begin
dm_data <= prdata;
state <= S_RDATA0;
end
S_RDATA0: if (tx_wrdy) begin
dm_data <= {rx_rdata, dm_data[31:8]};
state <= S_RDATA1;
end
S_RDATA1: if (tx_wrdy) begin
dm_data <= {rx_rdata, dm_data[31:8]};
state <= S_RDATA2;
end
S_RDATA2: if (tx_wrdy) begin
dm_data <= {rx_rdata, dm_data[31:8]};
state <= S_RDATA3;
end
S_RDATA3: if (tx_wrdy) begin
dm_data <= {rx_rdata, dm_data[31:8]};
state <= S_CMD;
end
endcase
end
// ----------------------------------------------------------------------------
// Bus & FIFO hookup
wire state_is_idle =
state == S_IDLE0 ||
state == S_IDLE1 ||
state == S_IDLE2 ||
state == S_IDLE3;
wire state_is_wdata =
state == S_WDATA0 ||
state == S_WDATA1 ||
state == S_WDATA2 ||
state == S_WDATA3;
wire state_is_rdata =
state == S_RDATA0 ||
state == S_RDATA1 ||
state == S_RDATA2 ||
state == S_RDATA3;
// Note we don't consume the read padding bytes during the read data phase --
// these are actually interpreted as NOPs preceding the next command.
// (They are still important for bus pacing though.)
assign rx_rrdy =
state_is_idle ||
state == S_CMD ||
state == S_WADDR ||
state == S_RADDR ||
state_is_wdata;
assign tx_wdata = state_is_wdata ? rx_rdata : dm_data[7:0];
assign tx_wvld = (state_is_wdata && state_is_wdata) || state_is_rdata;
assign psel =
state == S_WSETUP ||
state == S_WACCESS ||
state == S_RSETUP ||
state == S_RACCESS;
assign penable =
state == S_WACCESS ||
state == S_RACCESS;
assign pwrite =
state == S_WSETUP ||
state == S_WACCESS;
assign paddr = dm_addr;
assign pwdata = dm_data;
endmodule

View File

@ -0,0 +1,61 @@
/**********************************************************************
* DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE *
* Version 3, April 2008 *
* *
* Copyright (C) 2021 Luke Wren *
* *
* Everyone is permitted to copy and distribute verbatim or modified *
* copies of this license document and accompanying software, and *
* changing either is allowed. *
* *
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION *
* *
* 0. You just DO WHAT THE FUCK YOU WANT TO. *
* 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK. *
* *
*********************************************************************/
// Nothing to see here, just a sync FIFO
module hazard3_uart_dtm_fifo #(
parameter WIDTH = 8,
parameter LOG_DEPTH = 2
) (
input wire clk,
input wire rst_n,
input wire [WIDTH-1:0] wdata,
input wire wvld,
output wire wrdy,
output wire [WIDTH-1:0] rdata,
output wire rvld,
input wire rrdy
);
reg [WIDTH-1:0] fifo_mem [0:(1 << LOG_DEPTH) - 1];
reg [LOG_DEPTH:0] wptr;
reg [LOG_DEPTH:0] rptr;
assign wrdy = (rptr ^ {1'b1, {LOG_DEPTH{1'b0}}}) != wptr;
assign rvld = rptr != wptr;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
wptr <= {LOG_DEPTH+1{1'b0}};
rptr <= {LOG_DEPTH+1{1'b0}};
end else begin
if (wvld && wrdy) begin
fifo_mem[wptr[LOG_DEPTH-1:0]] <= wdata;
wptr <= wptr + 1'b1;
end
if (rvld && rrdy) begin
rptr <= rptr + 1'b1;
end
end
end
assign rdata = fifo_mem[rptr[LOG_DEPTH-1:0]];
endmodule

View File

@ -55,7 +55,7 @@ parameter NUM_IRQ = 32,
// ID registers
// JEDEC JEP106-compliant vendor ID, can be left at 0 if "not implemented or
// that this is a non-commercial implementation" (RISC-V spec).
// [...] this is a non-commercial implementation" (RISC-V spec).
// 31:7 is continuation code count, 6:0 is ID. Parity bit is not stored.
parameter MVENDORID_VAL = 32'h0,