Forráskód Böngészése

cores/usb: Rework CSR mapping and way events are reported

 - 'Actions' bits are now in a separate CSR
 - 'Events' can be reported to the host in a more flexible way.
    It can either be a simple flag, or a full FIFO of events depending
    on the core config

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 5 éve
szülő
commit
af7a3a57d6
3 módosított fájl, 219 hozzáadás és 103 törlés
  1. 59 11
      cores/usb/doc/mem-map.md
  2. 149 59
      cores/usb/rtl/usb.v
  3. 11 33
      cores/usb/rtl/usb_trans.v

+ 59 - 11
cores/usb/doc/mem-map.md

@@ -1,43 +1,91 @@
 iCE40 USB Core Memory Map
 =========================
 
-Global CSR
-----------
+Global CSRs
+-----------
 
-### Control (Write addr `0x000`)
+### Control (Read / Write addr `0x000`)
 
 ```
 ,--------------------------------------------------------------,
 | f | e | d | c | b | a | 9 | 8| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 |--------------------------------------------------------------|
-| p | / |       addr           |      (rsvd)               | a |
+| p | / | cs| ce|   (rsvd)             |        addr           |
 '--------------------------------------------------------------'
 ```
 
   * `p`: Enables DP pull-up
-  * `a`: Ack interrupt
+  * `cs`: Control Endpoint Lockout - State [Read Only]
+  * `ce`: Control Endpoint Lockout - Enable
+  * `addr`: Configure address matching
 
 
-### Status (Read addr `0x000`)
+### Action ( Write addr `0x01` )
 
 ```
 ,--------------------------------------------------------------,
 | f | e | d | c | b | a | 9 | 8| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 |--------------------------------------------------------------|
-|      cnt      |     ucnc     |      endp     | d | s | b | i |
+|  rsvd | cr|                  (rsvd)                          |
 '--------------------------------------------------------------'
 ```
 
-This contains info about the last generated notification from
-the transaction microcode
+  * `cr`: Control Endpoint Lockout - Release
+
+
+### Events (Read addr `0x02`)
+
+This contains info about the generated events from the transaction
+microcode.
+
+It can either contain info about the last event only along with a
+count of events since last read, or it can be a FIFO depending on
+the core configuration.
+
+Count mode (`EVENT_DEPTH = 0/1`) :
+
+```
+,--------------------------------------------------------------,
+| f | e | d | c | b | a | 9 | 8| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+|--------------------------------------------------------------|
+|      cnt      |                 event                        |
+'--------------------------------------------------------------'
+```
+
+  * `cnt`: Counter of events since last read
+  * `event`: Last recorded event data (see below)
+
+
+FIFO mode (`EVENT_DEPTH > 1`) :
+
+```
+,--------------------------------------------------------------,
+| f | e | d | c | b | a | 9 | 8| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+|--------------------------------------------------------------|
+| v | o |  rsvd |                 event                        |
+'--------------------------------------------------------------'
+```
+
+  * `v`: Valid (i.e. FIFO is not empty and `event` is valid)
+  * `o`: FIFO Overflow
+  * `event`: event data (see below)
+
+
+Event format:
+
+```
+,----------------------------------------------,
+| b | a | 9 | 8| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+|----------------------------------------------|
+|     ucnc     |      endp     | d | s | b | / |
+'----------------------------------------------'
+```
 
-  * `cnt`: Counter (incremented by 1 at each notify to detect misses)
   * `ucnc`: Notification code
   * `endp`: Endpoint #
   * `d`: Direction (1=IN, 0=OUT/SETUP)
   * `s`: Is SETUP ?
   * `b`: Buffer Descriptor index
-  * `i`: Interrupt flag
 
 
 EP Status

+ 149 - 59
cores/usb/rtl/usb.v

@@ -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

+ 11 - 33
cores/usb/rtl/usb_trans.v

@@ -71,13 +71,11 @@ module usb_trans (
 	output wire [15:0] eps_wrdata_0,
 	input  wire [15:0] eps_rddata_3,
 
-	// Config
+	// Config / Status
 	input  wire [ 6:0] cr_addr,
-	output wire [15:0] sr_notify,
 
-	output reg  irq_stb,
-	output wire irq_state,
-	input  wire irq_ack,
+	output wire [11:0] evt_data,
+	output wire evt_stb,
 
 	output wire cel_state,
 	input  wire cel_rel,
@@ -121,11 +119,6 @@ module usb_trans (
 	wire rto_now;
 	reg  [9:0] rto_cnt;
 
-	// Host notify
-	reg        irq_state_i;
-	reg  [3:0] irq_cnt;
-	reg [10:0] sr_notify_i;
-
 	// Transaction / EndPoint / Buffer infos
 	reg  [3:0] trans_pid;
 	reg        trans_is_setup;
@@ -270,31 +263,16 @@ module usb_trans (
 	// Host NOTIFY
 	// -----------
 
-	always @(posedge clk)
-		if (mc_op_notify)
-			sr_notify_i <= {
-				mc_opcode[3:0],				// Micro-code return value
-				trans_endp,					// Endpoint #
-				trans_dir,					// Direction
-				trans_is_setup,
-				ep_bd_idx_cur				// BD where it happenned
-			};
-
-	assign sr_notify = {
-		irq_cnt,
-		sr_notify_i,
-		irq_state
+	assign evt_stb = mc_op_notify;
+	assign evt_data = {
+		mc_opcode[3:0], // [11:8] Micro-code return value
+		trans_endp,     // [ 7:4] Endpoint
+		trans_dir,      //    [3] Direction
+		trans_is_setup, //    [2] SETUP transaction
+		ep_bd_idx_cur,  //    [1] BD where it happenned
+		1'b0
 	};
 
-	always @(posedge clk)
-	begin
-		irq_stb <= mc_op_notify;
-		irq_cnt <= irq_cnt + mc_op_notify;
-		irq_state_i <= (irq_state_i & ~irq_ack) | mc_op_notify;
-	end
-
-	assign irq_state = irq_state_i;
-
 
 	// EP infos
 	// --------