Add draft UART DTM
This commit is contained in:
parent
6a38fc33a6
commit
3312ea7022
|
@ -12,3 +12,5 @@ include::sections/introduction.adoc[]
|
|||
include::sections/instruction_timings.adoc[]
|
||||
|
||||
include::sections/csr.adoc[]
|
||||
|
||||
include::sections/debug.adoc[]
|
||||
|
|
3169
doc/hazard3.pdf
3169
doc/hazard3.pdf
File diff suppressed because it is too large
Load Diff
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
|
||||
|
|
Loading…
Reference in New Issue