|
@@ -28,6 +28,7 @@
|
|
|
module usb #(
|
|
|
parameter TARGET = "ICE40",
|
|
|
parameter integer EPDW = 16,
|
|
|
+ parameter integer EVT_DEPTH = 0,
|
|
|
|
|
|
/* Auto-set */
|
|
|
parameter integer EPAW = 11 - $clog2(EPDW / 8)
|
|
@@ -56,6 +57,9 @@ module usb #(
|
|
|
input wire bus_we,
|
|
|
output wire bus_ack,
|
|
|
|
|
|
+ // IRQ
|
|
|
+ output wire irq,
|
|
|
+
|
|
|
// Common
|
|
|
input wire clk,
|
|
|
input wire rst
|
|
@@ -133,30 +137,23 @@ module usb #(
|
|
|
|
|
|
wire eps_bus_ready;
|
|
|
reg eps_bus_read;
|
|
|
- reg eps_bus_zero;
|
|
|
+ wire eps_bus_zero;
|
|
|
reg eps_bus_write;
|
|
|
wire [15:0] eps_bus_dout;
|
|
|
|
|
|
// Config / Status registers
|
|
|
- reg cr_pu_ena;
|
|
|
- reg cr_cel_ena;
|
|
|
+ reg cr_pu_ena;
|
|
|
+ reg cr_cel_ena;
|
|
|
reg [ 6:0] cr_addr;
|
|
|
- wire [15:0] sr_notify;
|
|
|
-
|
|
|
- wire irq_stb;
|
|
|
- wire irq_state;
|
|
|
- reg irq_ack;
|
|
|
|
|
|
wire cel_state;
|
|
|
reg cel_rel;
|
|
|
|
|
|
// Bus interface
|
|
|
- reg eps_bus_req;
|
|
|
- wire eps_bus_clear;
|
|
|
|
|
|
- reg bus_ack_wait;
|
|
|
- wire bus_req_ok;
|
|
|
- reg [2:0] bus_req_ok_dly;
|
|
|
+ // Events
|
|
|
+ wire [11:0] evt_data;
|
|
|
+ wire evt_stb;
|
|
|
|
|
|
// Out-of-band conditions
|
|
|
wire oob_se0;
|
|
@@ -307,10 +304,8 @@ module usb #(
|
|
|
.eps_wrdata_0(eps_wrdata_0),
|
|
|
.eps_rddata_3(eps_rddata_3),
|
|
|
.cr_addr(cr_addr),
|
|
|
- .sr_notify(sr_notify),
|
|
|
- .irq_stb(irq_stb),
|
|
|
- .irq_state(irq_state),
|
|
|
- .irq_ack(irq_ack),
|
|
|
+ .evt_data(evt_data),
|
|
|
+ .evt_stb(evt_stb),
|
|
|
.cel_state(cel_state),
|
|
|
.cel_rel(cel_rel),
|
|
|
.cel_ena(cr_cel_ena),
|
|
@@ -375,87 +370,182 @@ module usb #(
|
|
|
);
|
|
|
|
|
|
|
|
|
- // Bus Interface
|
|
|
- // -------------
|
|
|
+ // CSR & Bus Interface
|
|
|
+ // -------------------
|
|
|
|
|
|
- wire [15:0] csr_dout;
|
|
|
+ reg csr_bus_req;
|
|
|
wire csr_bus_clear;
|
|
|
- reg csr_req;
|
|
|
+ wire csr_bus_ack;
|
|
|
+ reg [15:0] csr_bus_dout;
|
|
|
+
|
|
|
reg cr_bus_we;
|
|
|
- reg sr_bus_re;
|
|
|
|
|
|
- // Request lines for registers
|
|
|
+ reg eps_bus_req;
|
|
|
+ wire eps_bus_clear;
|
|
|
+ reg eps_bus_ack_wait;
|
|
|
+ wire eps_bus_req_ok;
|
|
|
+ reg [2:0] eps_bus_req_ok_dly;
|
|
|
+
|
|
|
+ wire [15:0] evt_rd_data;
|
|
|
+ wire evt_rd_rdy;
|
|
|
+ reg evt_rd_ack;
|
|
|
+
|
|
|
+ // Request lines for registers and strobes for actions
|
|
|
always @(posedge clk)
|
|
|
if (csr_bus_clear) begin
|
|
|
- csr_req <= 1'b0;
|
|
|
- cr_bus_we <= 1'b0;
|
|
|
- sr_bus_re <= 1'b0;
|
|
|
+ csr_bus_req <= 1'b0;
|
|
|
+ cr_bus_we <= 1'b0;
|
|
|
+ cel_rel <= 1'b0;
|
|
|
+ evt_rd_ack <= 1'b0;
|
|
|
end else begin
|
|
|
- csr_req <= ~bus_addr[11];
|
|
|
- cr_bus_we <= ~bus_addr[11] & bus_we;
|
|
|
- sr_bus_re <= ~bus_addr[11] & ~bus_we;
|
|
|
+ csr_bus_req <= 1'b1;
|
|
|
+ cr_bus_we <= (bus_addr[1:0] == 2'b00) & bus_we;
|
|
|
+ cel_rel <= (bus_addr[1:0] == 2'b01) & bus_we & bus_din[13];
|
|
|
+ evt_rd_ack <= (bus_addr[1:0] == 2'b10) & ~bus_we & evt_rd_rdy;
|
|
|
+ end
|
|
|
+
|
|
|
+ // Read mux for CSR
|
|
|
+ always @(posedge clk)
|
|
|
+ if (csr_bus_clear)
|
|
|
+ csr_bus_dout <= 16'h0000;
|
|
|
+ else
|
|
|
+ case (bus_addr[1:0])
|
|
|
+ 2'b00: csr_bus_dout <= { cr_pu_ena, 1'b0, cel_state, cr_cel_ena, 5'b00000, cr_addr } ;
|
|
|
+ 2'b10: csr_bus_dout <= evt_rd_data;
|
|
|
+ default: csr_bus_dout <= 16'h0000;
|
|
|
+ endcase
|
|
|
+
|
|
|
+ // CSR Clear/Ack
|
|
|
+ assign csr_bus_ack = csr_bus_req;
|
|
|
+ assign csr_bus_clear = ~bus_cyc | csr_bus_ack | bus_addr[11];
|
|
|
+
|
|
|
+ // Write regs
|
|
|
+ always @(posedge clk)
|
|
|
+ if (cr_bus_we) begin
|
|
|
+ cr_pu_ena <= bus_din[15];
|
|
|
+ cr_cel_ena <= bus_din[12];
|
|
|
+ cr_addr <= bus_din[5:0];
|
|
|
end
|
|
|
|
|
|
// Request lines for EP Status access
|
|
|
always @(posedge clk)
|
|
|
if (eps_bus_clear) begin
|
|
|
eps_bus_read <= 1'b0;
|
|
|
- eps_bus_zero <= 1'b1;
|
|
|
eps_bus_write <= 1'b0;
|
|
|
eps_bus_req <= 1'b0;
|
|
|
end else begin
|
|
|
eps_bus_read <= bus_addr[11] & ~bus_we;
|
|
|
- eps_bus_zero <= ~bus_addr[11];
|
|
|
eps_bus_write <= bus_addr[11] & bus_we;
|
|
|
eps_bus_req <= bus_addr[11];
|
|
|
end
|
|
|
|
|
|
- // Condition to force the requests to zero :
|
|
|
- // no access needed, ack pending or this cycle went through
|
|
|
- assign csr_bus_clear = ~bus_cyc | csr_req;
|
|
|
- assign eps_bus_clear = ~bus_cyc | bus_ack_wait | (eps_bus_req & eps_bus_ready);
|
|
|
+ assign eps_bus_zero = ~eps_bus_read;
|
|
|
+
|
|
|
+ // EPS Clear
|
|
|
+ assign eps_bus_clear = ~bus_cyc | eps_bus_ack_wait | (eps_bus_req & eps_bus_ready);
|
|
|
|
|
|
// Track when request are accepted by the RAM
|
|
|
- assign bus_req_ok = (eps_bus_req & eps_bus_ready);
|
|
|
+ assign eps_bus_req_ok = (eps_bus_req & eps_bus_ready);
|
|
|
|
|
|
always @(posedge clk)
|
|
|
- bus_req_ok_dly <= { bus_req_ok_dly[1:0], bus_req_ok & ~bus_we };
|
|
|
+ eps_bus_req_ok_dly <= { eps_bus_req_ok_dly[1:0], eps_bus_req_ok & ~bus_we };
|
|
|
|
|
|
// ACK wait state tracking
|
|
|
always @(posedge clk or posedge rst)
|
|
|
if (rst)
|
|
|
- bus_ack_wait <= 1'b0;
|
|
|
+ eps_bus_ack_wait <= 1'b0;
|
|
|
else
|
|
|
- bus_ack_wait <= ((bus_ack_wait & ~bus_we) | bus_req_ok) & ~bus_req_ok_dly[2];
|
|
|
+ eps_bus_ack_wait <= ((eps_bus_ack_wait & ~bus_we) | eps_bus_req_ok) & ~eps_bus_req_ok_dly[2];
|
|
|
|
|
|
// Bus Ack
|
|
|
- assign bus_ack = csr_req | (bus_ack_wait & (bus_we | bus_req_ok_dly[2]));
|
|
|
+ assign bus_ack = csr_bus_ack | (eps_bus_ack_wait & (bus_we | eps_bus_req_ok_dly[2]));
|
|
|
|
|
|
// Output is simply the OR of all local units since we force them to zero if
|
|
|
// they're not accessed
|
|
|
- assign bus_dout = eps_bus_dout | csr_dout;
|
|
|
+ assign bus_dout = csr_bus_dout | eps_bus_dout;
|
|
|
|
|
|
|
|
|
- // Config registers
|
|
|
- // ----------------
|
|
|
+ // Event handling
|
|
|
+ // --------------
|
|
|
|
|
|
- // Write regs
|
|
|
- always @(posedge clk)
|
|
|
- if (cr_bus_we) begin
|
|
|
- cr_pu_ena <= bus_din[15];
|
|
|
- cr_cel_ena <= bus_din[14];
|
|
|
- cr_addr <= bus_din[13:8];
|
|
|
- end
|
|
|
-
|
|
|
- // Write strobe
|
|
|
- always @(posedge clk)
|
|
|
- irq_ack <= cr_bus_we & bus_din[0];
|
|
|
-
|
|
|
- always @(posedge clk)
|
|
|
- cel_rel <= cr_bus_we & bus_din[1];
|
|
|
+ generate
|
|
|
+ if (EVT_DEPTH == 0) begin
|
|
|
+ // We just save the # of notify since last read
|
|
|
+ reg [3:0] evt_cnt;
|
|
|
+
|
|
|
+ always @(posedge clk or posedge rst)
|
|
|
+ if (rst)
|
|
|
+ evt_cnt <= 4'h0;
|
|
|
+ else
|
|
|
+ evt_cnt <= evt_rd_ack ? { 3'b000, evt_stb } : (evt_cnt + evt_stb);
|
|
|
+
|
|
|
+ assign evt_rd_rdy = 1'b1;
|
|
|
+ assign evt_rd_data = { evt_cnt, 12'h000 };
|
|
|
+
|
|
|
+ assign irq = (evt_cnt != 4'h0);
|
|
|
+
|
|
|
+ end else if (EVT_DEPTH == 1) begin
|
|
|
+ // Save the latest value and # of notify since last read
|
|
|
+ reg [11:0] evt_last;
|
|
|
+ reg [ 3:0] evt_cnt;
|
|
|
+
|
|
|
+ always @(posedge clk or posedge rst)
|
|
|
+ if (rst)
|
|
|
+ evt_cnt <= 4'h0;
|
|
|
+ else
|
|
|
+ evt_cnt <= evt_rd_ack ? { 3'b000, evt_stb } : (evt_cnt + evt_stb);
|
|
|
+
|
|
|
+ always @(posedge clk)
|
|
|
+ if (evt_stb)
|
|
|
+ evt_last <= evt_data;
|
|
|
+
|
|
|
+ assign evt_rd_rdy = 1'b1;
|
|
|
+ assign evt_rd_data = { evt_cnt, evt_last };
|
|
|
+
|
|
|
+ assign irq = (evt_cnt != 4'h0);
|
|
|
+
|
|
|
+ end else if (EVT_DEPTH > 1) begin
|
|
|
+ // Small shift-reg FIFO
|
|
|
+ wire [11:0] ef_wdata;
|
|
|
+ wire [11:0] ef_rdata;
|
|
|
+ wire ef_wren;
|
|
|
+ wire ef_full;
|
|
|
+ wire ef_rden;
|
|
|
+ wire ef_empty;
|
|
|
+
|
|
|
+ reg ef_overflow;
|
|
|
+
|
|
|
+ assign ef_wdata = evt_data;
|
|
|
+ assign ef_wren = evt_stb & ~ef_full;
|
|
|
+
|
|
|
+ always @(posedge clk or posedge rst)
|
|
|
+ if (rst)
|
|
|
+ ef_overflow <= 1'b0;
|
|
|
+ else
|
|
|
+ ef_overflow <= (ef_overflow & ~evt_rd_ack) | (evt_stb & ef_full);
|
|
|
+
|
|
|
+ assign evt_rd_rdy = ~ef_empty;
|
|
|
+ assign evt_rd_data = { ~ef_empty, ef_overflow, 2'b00, ef_rdata };
|
|
|
+ assign ef_rden = evt_rd_ack;
|
|
|
+
|
|
|
+ assign irq = ~ef_rden;
|
|
|
+
|
|
|
+ fifo_sync_shift #(
|
|
|
+ .DEPTH(EVT_DEPTH),
|
|
|
+ .WIDTH(12)
|
|
|
+ ) evt_fifo_I (
|
|
|
+ .wr_data(ef_wdata),
|
|
|
+ .wr_ena(ef_wren),
|
|
|
+ .wr_full(ef_full),
|
|
|
+ .rd_data(ef_rdata),
|
|
|
+ .rd_ena(ef_rden),
|
|
|
+ .rd_empty(ef_empty),
|
|
|
+ .clk(clk),
|
|
|
+ .rst(rst)
|
|
|
+ );
|
|
|
|
|
|
- // Read mux
|
|
|
- assign csr_dout = sr_bus_re ? sr_notify : 16'h0000;
|
|
|
+ end
|
|
|
+ endgenerate
|
|
|
|
|
|
|
|
|
// USB reset/suspend
|