URL
https://opencores.org/ocsvn/ulpi_wrapper/ulpi_wrapper/trunk
Subversion Repositories ulpi_wrapper
Compare Revisions
- This comparison shows the changes necessary to convert path
/ulpi_wrapper
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/trunk/testbench/simulation.svh
0,0 → 1,127
`timescale 1ns/1ps |
|
//----------------------------------------------------------------- |
// assert_task |
//----------------------------------------------------------------- |
task automatic assert_task(input v, string file, int line, input string s); |
begin |
if (!v) |
begin |
$display("ASSERT: %s:%0d - %s", file, line, s); |
$finish(1); |
end |
end |
endtask |
|
//----------------------------------------------------------------- |
// ASSERT |
//----------------------------------------------------------------- |
`define ASSERT(v) assert_task(v, `__FILE__, `__LINE__, `"v`") |
|
//----------------------------------------------------------------- |
// ASSERT_ALWAYS |
//----------------------------------------------------------------- |
`define ASSERT_ALWAYS(condition) \ |
generate \ |
if(1) \ |
begin \ |
wire test = condition; \ |
always @(test)\ |
`ASSERT(condition); \ |
end \ |
endgenerate |
|
//----------------------------------------------------------------- |
// ASSERT_STABLE |
//----------------------------------------------------------------- |
`define ASSERT_STABLE(CLK, RST, WIDTH, NAME, HOLD, VALID) \ |
reg [``WIDTH-1:0] v_stable_``NAME``; \ |
reg v_stable_``NAME``_valid; \ |
\ |
always @(posedge ``RST or posedge ``CLK) \ |
if (``RST) \ |
begin \ |
v_stable_``NAME`` <= ``WIDTH'b0; \ |
v_stable_``NAME``_valid <= 1'b0; \ |
end \ |
else \ |
begin \ |
if (``HOLD) \ |
begin \ |
if (v_stable_``NAME``_valid) \ |
begin \ |
`ASSERT(v_stable_``NAME`` === ``NAME); \ |
end \ |
v_stable_``NAME``_valid <= ``VALID; \ |
v_stable_``NAME`` <= ``NAME; \ |
end \ |
else \ |
begin \ |
if (v_stable_``NAME``_valid) \ |
begin \ |
`ASSERT(v_stable_``NAME`` === ``NAME); \ |
end \ |
v_stable_``NAME``_valid <= 1'b0; \ |
end \ |
end |
|
//----------------------------------------------------------------- |
// TB_TIMEOUT |
//----------------------------------------------------------------- |
`define TB_TIMEOUT(CLK, RST, VALID, TIMEOUT) \ |
integer v_timeout_cycles; \ |
\ |
always @(posedge ``RST or posedge ``CLK) \ |
if (``RST) \ |
v_timeout_cycles <= 0; \ |
else \ |
begin \ |
if (``VALID) \ |
begin \ |
v_timeout_cycles <= 0; \ |
end \ |
else \ |
begin \ |
v_timeout_cycles <= v_timeout_cycles + 1; \ |
`ASSERT(v_timeout_cycles < ``TIMEOUT); \ |
end \ |
end |
|
|
//----------------------------------------------------------------- |
// CLOCK_GEN |
//----------------------------------------------------------------- |
`define CLOCK_GEN(NAME, CYCLE) \ |
reg ``NAME; \ |
initial \ |
begin \ |
``NAME <= 0; \ |
forever # (``CYCLE / 2) ``NAME = ~``NAME; \ |
end |
|
//----------------------------------------------------------------- |
// RESET_GEN |
//----------------------------------------------------------------- |
`define RESET_GEN(NAME, DELAY) \ |
reg ``NAME; \ |
initial \ |
begin \ |
``NAME <= 1; \ |
# ``DELAY \ |
``NAME <= 0; \ |
end |
|
//----------------------------------------------------------------- |
// TB_VCD |
//----------------------------------------------------------------- |
`define TB_VCD(TOP, NAME) \ |
initial \ |
begin \ |
$dumpfile(``NAME); \ |
$dumpvars(0,``TOP); \ |
end |
|
//----------------------------------------------------------------- |
// TB_RUN_FOR |
//----------------------------------------------------------------- |
`define TB_RUN_FOR(TIME) initial #``TIME $finish; |
/trunk/testbench/ulpi_utmi.v
0,0 → 1,397
//----------------------------------------------------------------- |
// Module |
//----------------------------------------------------------------- |
module ulpi_utmi |
( |
input clk_i, |
input rst_i, |
|
// ULPI Interface (Input) |
input [7:0] ulpi_data_i, |
output [7:0] ulpi_data_o, |
output ulpi_dir_o, |
output ulpi_nxt_o, |
input ulpi_stp_i, |
|
// UTMI Interface |
output [7:0] utmi_data_o, |
output utmi_txvalid_o, |
input utmi_txready_i, |
input [7:0] utmi_data_i, |
input utmi_rxvalid_i, |
input utmi_rxactive_i, |
input utmi_rxerror_i, |
|
input [1:0] utmi_linestate_i, |
|
output utmi_reset_o, |
output [1:0] utmi_xcvrselect_o, |
output utmi_termselect_o, |
output [1:0] utmi_opmode_o |
); |
|
//----------------------------------------------------------------- |
// Params |
//----------------------------------------------------------------- |
parameter ULPI_VID = 16'h0424; |
parameter ULPI_PID = 16'h0004; |
parameter ULPI_FUNC_CTRL_DEF = 8'h41; |
|
//----------------------------------------------------------------- |
// Register Map |
//----------------------------------------------------------------- |
localparam ULPI_REG_VID_L = 6'h00; |
localparam ULPI_REG_VID_H = 6'h01; |
localparam ULPI_REG_PID_L = 6'h02; |
localparam ULPI_REG_PID_H = 6'h03; |
localparam ULPI_REG_FUNC_CTRL = 6'h04; |
localparam ULPI_REG_DEBUG = 6'h15; |
localparam ULPI_REG_SCRATCH = 6'h16; |
|
//----------------------------------------------------------------- |
// Registers / Wires |
//----------------------------------------------------------------- |
|
// SM states |
localparam STATE_W = 4; |
localparam STATE_IDLE = 4'd0; |
localparam STATE_RXCMD = 4'd1; |
localparam STATE_RXDATA = 4'd2; |
localparam STATE_TXCMD = 4'd3; |
localparam STATE_TXDATA = 4'd4; |
localparam STATE_REG_WR = 4'd5; |
localparam STATE_REG_RD = 4'd6; |
|
reg [STATE_W-1:0] state_q; |
reg [STATE_W-1:0] next_state_r; |
|
// Commands |
localparam CMD_IDLE = 2'b00; |
localparam CMD_TX = 2'b01; |
localparam CMD_REG_WR = 2'b10; |
localparam CMD_REG_RD = 2'b11; |
|
reg [7:0] tx_data_q; |
reg tx_valid_q; |
|
reg [7:0] rx_data_q; |
reg rx_valid_q; |
|
reg [7:0] utmi_data_r; |
reg utmi_txvalid_r; |
|
reg [1:0] linestate_q; |
|
//----------------------------------------------------------------- |
// Turnaround detection |
//----------------------------------------------------------------- |
reg ulpi_dir_q; |
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
ulpi_dir_q <= 1'b0; |
else |
ulpi_dir_q <= ulpi_dir_o; |
|
wire turnaround_w = ulpi_dir_q ^ ulpi_dir_o; |
|
//----------------------------------------------------------------- |
// Linestate change detect |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
linestate_q <= 2'b0; |
else if ((state_q == STATE_RXCMD) || (state_q == STATE_RXDATA && !rx_valid_q)) |
linestate_q <= utmi_linestate_i; |
|
wire linestate_update_w = (linestate_q != utmi_linestate_i); |
|
//----------------------------------------------------------------- |
// State Machine |
//----------------------------------------------------------------- |
always @ * |
begin |
next_state_r = state_q; |
|
case (state_q) |
//----------------------------------------- |
// STATE_IDLE |
//----------------------------------------- |
STATE_IDLE : |
begin |
if (utmi_rxactive_i) |
next_state_r = STATE_RXCMD; |
else if (ulpi_data_i[7:6] == CMD_TX) |
next_state_r = STATE_TXCMD; |
else if (ulpi_data_i[7:6] == CMD_REG_WR) |
next_state_r = STATE_REG_WR; |
else if (ulpi_data_i[7:6] == CMD_REG_RD) |
next_state_r = STATE_REG_RD; |
else if (linestate_update_w) |
next_state_r = STATE_RXCMD; |
end |
//----------------------------------------- |
// STATE_REG_WR |
//----------------------------------------- |
STATE_REG_WR : |
begin |
next_state_r = STATE_IDLE; |
end |
//----------------------------------------- |
// STATE_REG_RD |
//----------------------------------------- |
STATE_REG_RD : |
begin |
next_state_r = STATE_IDLE; |
end |
//----------------------------------------- |
// STATE_RXCMD |
//----------------------------------------- |
STATE_RXCMD : |
begin |
if (!utmi_rxactive_i) |
next_state_r = STATE_IDLE; |
else |
next_state_r = STATE_RXDATA; |
end |
//----------------------------------------- |
// STATE_RXDATA |
//----------------------------------------- |
STATE_RXDATA : |
begin |
if (!utmi_rxactive_i) |
next_state_r = STATE_RXCMD; |
end |
//----------------------------------------- |
// STATE_TXCMD |
//----------------------------------------- |
STATE_TXCMD : |
begin |
if (ulpi_nxt_o) |
next_state_r = STATE_TXDATA; |
end |
//----------------------------------------- |
// STATE_TXDATA |
//----------------------------------------- |
STATE_TXDATA : |
begin |
if (ulpi_stp_i) |
next_state_r = STATE_IDLE; |
end |
default: |
; |
endcase |
end |
|
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
state_q <= STATE_IDLE; |
else if (!turnaround_w) |
state_q <= next_state_r; |
|
//----------------------------------------------------------------- |
// Register Access |
//----------------------------------------------------------------- |
reg [5:0] reg_addr_q; |
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
reg_addr_q <= 6'b0; |
else if (state_q == STATE_IDLE && ulpi_data_i[7:6] == CMD_REG_WR) |
reg_addr_q <= ulpi_data_i[5:0]; |
|
wire reg_wr_w = (state_q == STATE_REG_WR); |
wire [7:0] reg_data_w = ulpi_data_i; |
reg [7:0] reg_data_r; |
|
//----------------------------------------------------------------- |
// Register: Function Control |
//----------------------------------------------------------------- |
reg [7:0] func_ctrl_q; |
|
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
func_ctrl_q <= ULPI_FUNC_CTRL_DEF; |
else if (reg_wr_w && reg_addr_q == ULPI_REG_FUNC_CTRL) |
func_ctrl_q <= ulpi_data_i; |
else |
func_ctrl_q <= {3'b0, func_ctrl_q[4:0]}; |
|
assign utmi_xcvrselect_o = func_ctrl_q[1:0]; |
assign utmi_termselect_o = func_ctrl_q[2]; |
assign utmi_opmode_o = func_ctrl_q[4:3]; |
assign utmi_reset_o = func_ctrl_q[5]; |
|
//----------------------------------------------------------------- |
// Register: Scratch |
//----------------------------------------------------------------- |
reg [7:0] reg_scratch_q; |
|
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
reg_scratch_q <= 8'b0; |
else if (reg_wr_w && reg_addr_q == ULPI_REG_SCRATCH) |
reg_scratch_q <= ulpi_data_i; |
|
//----------------------------------------------------------------- |
// Register read mux |
//----------------------------------------------------------------- |
always @ * |
begin |
reg_data_r = 8'b0; |
|
case (reg_addr_q) |
ULPI_REG_VID_L: reg_data_r = ULPI_VID[7:0]; |
ULPI_REG_VID_H: reg_data_r = ULPI_VID[15:8]; |
ULPI_REG_PID_L: reg_data_r = ULPI_PID[7:0]; |
ULPI_REG_PID_H: reg_data_r = ULPI_PID[15:8]; |
ULPI_REG_FUNC_CTRL: reg_data_r = func_ctrl_q; |
ULPI_REG_DEBUG: reg_data_r = {6'b0, utmi_linestate_i}; |
ULPI_REG_SCRATCH: reg_data_r = reg_scratch_q; |
default: reg_data_r = 8'b0; |
endcase |
end |
|
//----------------------------------------------------------------- |
// Receive Buffer |
//----------------------------------------------------------------- |
always @ (posedge rst_i or posedge clk_i) |
if (rst_i) |
begin |
rx_data_q <= 8'b0; |
rx_valid_q <= 1'b0; |
end |
else if (utmi_rxactive_i && utmi_rxvalid_i) |
begin |
rx_data_q <= utmi_data_i; |
rx_valid_q <= 1'b1; |
end |
else |
rx_valid_q <= 1'b0; |
|
//----------------------------------------------------------------- |
// Output Mux |
//----------------------------------------------------------------- |
reg [7:0] ulpi_data_r; |
reg ulpi_dir_r; |
reg ulpi_nxt_r; |
|
always @ * |
begin |
ulpi_data_r = 8'b0; |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = 1'b0; |
|
case (state_q) |
//----------------------------------------- |
// STATE_IDLE |
//----------------------------------------- |
STATE_IDLE : |
begin |
if (utmi_rxactive_i) |
begin |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = 1'b0; |
end |
else if (ulpi_data_i[7:6] == CMD_TX) |
begin |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = 1'b0; |
end |
else if (ulpi_data_i[7:6] == CMD_REG_WR) |
begin |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = 1'b1; |
end |
end |
STATE_RXCMD: |
begin |
ulpi_dir_r = 1'b1; |
ulpi_data_r[1:0] = utmi_linestate_i; |
case ({utmi_rxactive_i, utmi_rxerror_i}) |
2'b00: ulpi_data_r[5:4] = 2'b00; |
2'b10: ulpi_data_r[5:4] = 2'b01; |
2'b11: ulpi_data_r[5:4] = 2'b11; |
default: ulpi_data_r[5:4] = 2'b00; |
endcase |
|
ulpi_nxt_r = 1'b0; |
end |
STATE_REG_WR: |
begin |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = 1'b1; |
end |
STATE_REG_RD: |
begin |
ulpi_dir_r = 1'b1; |
ulpi_nxt_r = 1'b0; |
ulpi_data_r = reg_data_r; |
end |
STATE_TXCMD: |
begin |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = /*!turnaround_w && */utmi_txready_i; |
end |
STATE_TXDATA: |
begin |
ulpi_dir_r = 1'b0; |
ulpi_nxt_r = utmi_txready_i; |
end |
STATE_RXDATA: |
begin |
if (rx_valid_q) |
begin |
ulpi_dir_r = 1'b1; |
ulpi_data_r = rx_data_q; |
ulpi_nxt_r = 1'b1; |
end |
else |
begin |
ulpi_dir_r = 1'b1; |
ulpi_data_r[1:0] = utmi_linestate_i; |
case ({utmi_rxactive_i, utmi_rxerror_i}) |
2'b00: ulpi_data_r[5:4] = 2'b00; |
2'b10: ulpi_data_r[5:4] = 2'b01; |
2'b11: ulpi_data_r[5:4] = 2'b11; |
default: ulpi_data_r[5:4] = 2'b00; |
endcase |
|
ulpi_nxt_r = 1'b0; |
end |
end |
default: |
; |
endcase |
end |
|
assign ulpi_data_o = ulpi_data_r; |
assign ulpi_dir_o = ulpi_dir_r; |
assign ulpi_nxt_o = ulpi_nxt_r; |
|
//----------------------------------------------------------------- |
// UTMI Output |
//----------------------------------------------------------------- |
always @ * |
begin |
utmi_data_r = 8'b0; |
utmi_txvalid_r = 1'b0; |
|
case (state_q) |
STATE_TXCMD: |
begin |
utmi_data_r = {~ulpi_data_i[3:0], ulpi_data_i[3:0]}; |
utmi_txvalid_r = !turnaround_w; |
end |
STATE_TXDATA: |
begin |
utmi_data_r = ulpi_data_i; |
utmi_txvalid_r = !ulpi_stp_i; |
end |
default: |
; |
endcase |
end |
|
assign utmi_data_o = utmi_data_r; |
assign utmi_txvalid_o = utmi_txvalid_r; |
|
endmodule |
/trunk/testbench/makefile
0,0 → 1,35
SEED ?= 1 |
CYCLES ?= 200000 |
TRACE ?= 1 |
|
all: compile run view |
|
# Testbench |
SRC+= ./top_tb.sv |
SRC+= ./*.v |
|
SRC+= ../rtl/*.v |
|
SRC_FLAGS = +define+CYCLES=$(CYCLES) |
SRC_FLAGS = +define+SEED=$(SEED) |
|
ifeq ($(TRACE),1) |
SRC_FLAGS += +define+TRACE=$(TRACE) |
endif |
|
INC_DIRS = -I. |
|
compile : |
vlib work |
vlog $(SRC) $(SRC_FLAGS) |
|
run : compile |
vsim -c -do "run -all" top_tb |
|
view : compile |
ifeq ($(TRACE),1) |
gtkwave waveform.vcd gtksettings.sav |
endif |
|
clean : |
-rm -rf work waveform.vcd transcript |
/trunk/testbench/gtksettings.sav
0,0 → 1,57
@28 |
top_tb.clk |
top_tb.rst |
@200 |
- |
@28 |
top_tb.switch_pending |
@22 |
top_tb.master_sel[31:0] |
@200 |
- |
- |
@800200 |
-LINK |
@28 |
top_tb.utmi_link_txvalid |
top_tb.utmi_link_txready |
@22 |
top_tb.utmi_link_data_in[7:0] |
@28 |
top_tb.utmi_link_rxerror |
top_tb.utmi_link_rxvalid |
top_tb.utmi_link_rxactive |
@22 |
#{top_tb.utmi_link_data_out[7:0]} top_tb.utmi_link_data_out[7] top_tb.utmi_link_data_out[6] top_tb.utmi_link_data_out[5] top_tb.utmi_link_data_out[4] top_tb.utmi_link_data_out[3] top_tb.utmi_link_data_out[2] top_tb.utmi_link_data_out[1] top_tb.utmi_link_data_out[0] |
@1000200 |
-LINK |
@200 |
- |
@29 |
top_tb.ulpi_dir |
@22 |
#{top_tb.ulpi_data_in[7:0]} top_tb.ulpi_data_in[7] top_tb.ulpi_data_in[6] top_tb.ulpi_data_in[5] top_tb.ulpi_data_in[4] top_tb.ulpi_data_in[3] top_tb.ulpi_data_in[2] top_tb.ulpi_data_in[1] top_tb.ulpi_data_in[0] |
#{top_tb.ulpi_data_out[7:0]} top_tb.ulpi_data_out[7] top_tb.ulpi_data_out[6] top_tb.ulpi_data_out[5] top_tb.ulpi_data_out[4] top_tb.ulpi_data_out[3] top_tb.ulpi_data_out[2] top_tb.ulpi_data_out[1] top_tb.ulpi_data_out[0] |
@28 |
top_tb.ulpi_nxt |
top_tb.ulpi_stp |
@200 |
- |
@800200 |
-PHY |
@28 |
top_tb.utmi_phy_rxactive |
top_tb.utmi_phy_rxvalid |
@22 |
top_tb.utmi_phy_data_in[7:0] |
@200 |
- |
@28 |
top_tb.utmi_phy_txvalid |
top_tb.utmi_phy_txready |
@22 |
#{top_tb.utmi_phy_data_out[7:0]} top_tb.utmi_phy_data_out[7] top_tb.utmi_phy_data_out[6] top_tb.utmi_phy_data_out[5] top_tb.utmi_phy_data_out[4] top_tb.utmi_phy_data_out[3] top_tb.utmi_phy_data_out[2] top_tb.utmi_phy_data_out[1] top_tb.utmi_phy_data_out[0] |
@1000200 |
-PHY |
[pattern_trace] 1 |
[pattern_trace] 0 |
/trunk/testbench/top_tb.sv
0,0 → 1,299
`timescale 1ns/100ps |
|
//----------------------------------------------------------------- |
// Module |
//----------------------------------------------------------------- |
module top_tb ; |
|
//----------------------------------------------------------------- |
// Simulation |
//----------------------------------------------------------------- |
`include "simulation.svh" |
|
`CLOCK_GEN(clk, 16.666) |
`RESET_GEN(rst, 16.666) |
|
`ifdef TRACE |
`TB_VCD(top_tb, "waveform.vcd") |
`endif |
|
`TB_RUN_FOR(10ms) |
|
//----------------------------------------------------------------- |
// Registers / Wires |
//----------------------------------------------------------------- |
reg [7:0] utmi_link_data_in; |
wire [7:0] utmi_link_data_out; |
reg utmi_link_txvalid; |
wire utmi_link_txready; |
wire utmi_link_rxvalid; |
wire utmi_link_rxactive; |
wire utmi_link_rxerror; |
wire [1:0] utmi_link_linestate; |
|
wire [7:0] utmi_phy_data_out; |
reg [7:0] utmi_phy_data_in; |
reg [1:0] utmi_phy_linestate; |
wire utmi_phy_txvalid; |
reg utmi_phy_txready; |
reg utmi_phy_rxvalid; |
reg utmi_phy_rxactive; |
|
wire [7:0] ulpi_data_in; |
wire [7:0] ulpi_data_out; |
wire ulpi_dir; |
wire ulpi_nxt; |
wire ulpi_stp; |
|
reg [7:0] queue_for_link[$]; |
reg [7:0] queue_for_phy[$]; |
|
|
//----------------------------------------------------------------- |
// DUT |
//----------------------------------------------------------------- |
// UTMI -> ULPI |
ulpi_wrapper |
u_utmi_ulpi |
( |
// ULPI Interface (Output) |
.ulpi_clk60_i(clk), |
.ulpi_rst_i(rst), |
.ulpi_data_i(ulpi_data_in), |
.ulpi_data_o(ulpi_data_out), |
.ulpi_dir_i(ulpi_dir), |
.ulpi_nxt_i(ulpi_nxt), |
.ulpi_stp_o(ulpi_stp), |
|
// UTMI Control Pins |
.utmi_xcvrselect_i(2'b11), |
.utmi_termselect_i(1'b0), |
.utmi_opmode_i(2'b0), |
.utmi_dppulldown_i(1'b0), |
.utmi_dmpulldown_i(1'b0), |
|
// UTMI Interface |
.utmi_data_i(utmi_link_data_in), |
.utmi_txvalid_i(utmi_link_txvalid), |
.utmi_txready_o(utmi_link_txready), |
.utmi_data_o(utmi_link_data_out), |
.utmi_rxvalid_o(utmi_link_rxvalid), |
.utmi_rxactive_o(utmi_link_rxactive), |
.utmi_rxerror_o(utmi_link_rxerror), |
.utmi_linestate_o(utmi_link_linestate) |
); |
|
//----------------------------------------------------------------- |
// TB Components |
//----------------------------------------------------------------- |
// ULPI -> UTMI |
ulpi_utmi |
u_ulpi_utmi |
( |
.clk_i(clk), |
.rst_i(rst), |
|
// ULPI Interface (Input) |
.ulpi_data_i(ulpi_data_out), |
.ulpi_data_o(ulpi_data_in), |
.ulpi_dir_o(ulpi_dir), |
.ulpi_nxt_o(ulpi_nxt), |
.ulpi_stp_i(ulpi_stp), |
|
// UTMI Interface (Output) |
.utmi_data_o(utmi_phy_data_out), |
.utmi_txvalid_o(utmi_phy_txvalid), |
.utmi_txready_i(utmi_phy_txready), |
.utmi_data_i(utmi_phy_data_in), |
.utmi_rxvalid_i(utmi_phy_rxvalid), |
.utmi_rxactive_i(utmi_phy_rxactive), |
.utmi_rxerror_i(1'b0), |
.utmi_linestate_i(utmi_phy_linestate), |
|
// UTMI Control Pins |
.utmi_reset_o(), |
.utmi_xcvrselect_o(), |
.utmi_termselect_o(), |
.utmi_opmode_o() |
); |
|
//----------------------------------------------------------------- |
// Master selector |
//----------------------------------------------------------------- |
integer master_sel; |
reg new_master_sel; |
|
reg switch_pending; |
|
always @(posedge clk or posedge rst) |
begin |
if (rst) |
begin |
master_sel <= 0; |
new_master_sel <= 1'b1; |
switch_pending <= 1'b0; |
end |
else |
begin |
if (switch_pending) |
begin |
if (!utmi_phy_rxactive && !utmi_phy_txvalid && |
!utmi_link_rxactive && !utmi_link_txvalid && |
queue_for_link.size() == 0 && queue_for_phy.size() == 0) |
begin |
master_sel <= !master_sel; |
switch_pending <= 1'b0; |
end |
end |
else if ($urandom_range(1000,0) == 0) |
begin |
switch_pending <= 1'b1; |
end |
end |
end |
|
//----------------------------------------------------------------- |
// LINK: Traffic generator |
//----------------------------------------------------------------- |
always @(posedge clk or posedge rst) |
begin |
if (rst) |
begin |
utmi_link_data_in <= 8'h00; |
utmi_link_txvalid <= 1'b0; |
end |
else |
begin |
// LINK -> PHY |
if ($urandom_range(8,0) == 0 && ~utmi_link_txvalid && (master_sel == 0) && !switch_pending) |
begin |
reg [3:0] pid; |
pid = $urandom; |
utmi_link_data_in[7:4] <= ~pid; |
utmi_link_data_in[3:0] <= pid; |
utmi_link_txvalid <= 1'b1; |
end |
else if (utmi_link_txvalid && utmi_link_txready) |
begin |
// End of packet |
if ($urandom_range(8,0) == 0) |
begin |
utmi_link_txvalid <= 1'b0; |
end |
// Next data byte in packet |
else if (!switch_pending) |
begin |
utmi_link_data_in <= $urandom_range(255,0); |
utmi_link_txvalid <= 1'b1; |
end |
else |
begin |
utmi_link_txvalid <= 1'b0; |
end |
end |
end |
end |
|
//----------------------------------------------------------------- |
// PHY: Traffic generator |
//----------------------------------------------------------------- |
reg utmi_phy_rxactive_d; |
integer phy_backoff; |
|
always @(posedge clk or posedge rst) |
begin |
if (rst) |
begin |
utmi_phy_data_in <= 8'h00; |
utmi_phy_rxactive <= 1'b0; |
utmi_phy_rxvalid <= 1'b0; |
utmi_phy_txready <= 1'b0; |
utmi_phy_rxactive_d <= 1'b0; |
utmi_phy_linestate <= 2'b0; |
phy_backoff <= 0; |
end |
else |
begin |
utmi_phy_txready <= $urandom; |
utmi_phy_linestate <= 2'b01; |
utmi_phy_rxactive_d <= utmi_phy_rxactive; |
|
// PHY -> LINK |
if ($urandom_range(8,0) == 0 && ~utmi_phy_rxactive && (master_sel == 1) && !switch_pending && phy_backoff == 0) |
begin |
utmi_phy_rxactive <= 1'b1; |
end |
else if (utmi_phy_rxactive && utmi_phy_rxactive_d) |
begin |
utmi_phy_rxvalid <= 1'b0; |
|
// End of packet |
if ($urandom_range(8,0) == 0) |
begin |
utmi_phy_rxactive <= 1'b0; |
phy_backoff <= 8; |
end |
// Next data byte in packet |
else if (!switch_pending && $urandom_range(8,0) != 0) |
begin |
utmi_phy_data_in <= $urandom_range(255,0); |
utmi_phy_rxvalid <= 1'b1; |
end |
end |
else if (phy_backoff > 0) |
phy_backoff <= phy_backoff - 1; |
end |
end |
|
|
//----------------------------------------------------------------- |
// LINK: Checker |
//----------------------------------------------------------------- |
always @(posedge clk) |
begin |
if (!rst) |
begin |
if (utmi_link_txvalid && utmi_link_txready) |
begin |
queue_for_phy.push_back(utmi_link_data_in); |
end |
|
if (utmi_link_rxvalid && utmi_link_rxactive) |
begin |
reg [7:0] head; |
|
`ASSERT(queue_for_link.size() > 0); |
|
head = queue_for_link.pop_front(); |
`ASSERT(head === utmi_link_data_out); |
end |
end |
end |
|
//----------------------------------------------------------------- |
// PHY: Checker |
//----------------------------------------------------------------- |
always @(posedge clk) |
begin |
if (!rst) |
begin |
if (utmi_phy_rxactive && utmi_phy_rxvalid) |
begin |
queue_for_link.push_back(utmi_phy_data_in); |
end |
|
if (utmi_phy_txvalid && utmi_phy_txready) |
begin |
reg [7:0] head; |
|
`ASSERT(queue_for_phy.size() > 0); |
|
head = queue_for_phy.pop_front(); |
`ASSERT(head === utmi_phy_data_out); |
end |
end |
end |
|
`TB_TIMEOUT(clk, rst, !utmi_link_rxvalid && !utmi_phy_rxvalid, 10000); |
|
endmodule |
/trunk/rtl/ulpi_wrapper.v
0,0 → 1,379
//----------------------------------------------------------------- |
// ULPI (Link) Wrapper |
// V0.1 |
// Ultra-Embedded.com |
// Copyright 2015 |
// |
// Email: admin@ultra-embedded.com |
// |
// License: GPL |
// If you would like a version with a more permissive license for |
// use in closed source commercial applications please contact me |
// for details. |
//----------------------------------------------------------------- |
// |
// This file is open source HDL; you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as |
// published by the Free Software Foundation; either version 2 of |
// the License, or (at your option) any later version. |
// |
// This file is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// |
// You should have received a copy of the GNU General Public |
// License along with this file; if not, write to the Free Software |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
// USA |
//----------------------------------------------------------------- |
|
//----------------------------------------------------------------- |
// Module: UTMI+ to ULPI Wrapper |
// |
// Description: |
// - Converts from UTMI interface to reduced pin count ULPI. |
// - No support for low power mode. |
// - I/O synchronous to 60MHz ULPI clock input (from PHY) |
// - Tested against SMSC/Microchip USB3300 in device mode. |
//----------------------------------------------------------------- |
module ulpi_wrapper |
( |
// ULPI Interface (PHY) |
input ulpi_clk60_i, |
input ulpi_rst_i, |
input [7:0] ulpi_data_i, |
output [7:0] ulpi_data_o, |
input ulpi_dir_i, |
input ulpi_nxt_i, |
output ulpi_stp_o, |
|
// UTMI Interface (SIE) |
input utmi_txvalid_i, |
output utmi_txready_o, |
output utmi_rxvalid_o, |
output utmi_rxactive_o, |
output utmi_rxerror_o, |
output [7:0] utmi_data_o, |
input [7:0] utmi_data_i, |
input [1:0] utmi_xcvrselect_i, |
input utmi_termselect_i, |
input [1:0] utmi_opmode_i, |
input utmi_dppulldown_i, |
input utmi_dmpulldown_i, |
output [1:0] utmi_linestate_o |
); |
|
//----------------------------------------------------------------- |
// States |
//----------------------------------------------------------------- |
localparam STATE_W = 2; |
localparam STATE_IDLE = 2'd0; |
localparam STATE_CMD = 2'd1; |
localparam STATE_DATA = 2'd2; |
localparam STATE_WAIT = 2'd3; |
|
reg [STATE_W-1:0] state_q; |
|
//----------------------------------------------------------------- |
// UTMI Mode Select |
//----------------------------------------------------------------- |
reg mode_update_q; |
reg [1:0] xcvrselect_q; |
reg termselect_q; |
reg [1:0] opmode_q; |
reg phy_reset_q; |
|
always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) |
if (ulpi_rst_i) |
begin |
mode_update_q <= 1'b0; |
xcvrselect_q <= 2'b0; |
termselect_q <= 1'b0; |
opmode_q <= 2'b11; |
phy_reset_q <= 1'b1; |
end |
else |
begin |
xcvrselect_q <= utmi_xcvrselect_i; |
termselect_q <= utmi_termselect_i; |
opmode_q <= utmi_opmode_i; |
|
if (mode_update_q && (state_q == STATE_IDLE) && !ulpi_dir_i) |
begin |
mode_update_q <= 1'b0; |
phy_reset_q <= 1'b0; |
end |
else if (opmode_q != utmi_opmode_i || |
termselect_q != utmi_termselect_i || |
xcvrselect_q != utmi_xcvrselect_i) |
mode_update_q <= 1'b1; |
end |
|
//----------------------------------------------------------------- |
// UTMI OTG Control |
//----------------------------------------------------------------- |
reg otg_update_q; |
reg dppulldown_q; |
reg dmpulldown_q; |
|
always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) |
if (ulpi_rst_i) |
begin |
otg_update_q <= 1'b0; |
dppulldown_q <= 1'b1; |
dmpulldown_q <= 1'b1; |
end |
else |
begin |
dppulldown_q <= utmi_dppulldown_i; |
dmpulldown_q <= utmi_dmpulldown_i; |
|
if (otg_update_q && !mode_update_q && (state_q == STATE_IDLE) && !ulpi_dir_i) |
otg_update_q <= 1'b0; |
else if (dppulldown_q != utmi_dppulldown_i || |
dmpulldown_q != utmi_dmpulldown_i) |
otg_update_q <= 1'b1; |
end |
|
//----------------------------------------------------------------- |
// Bus turnaround detect |
//----------------------------------------------------------------- |
reg ulpi_dir_q; |
|
always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) |
if (ulpi_rst_i) |
ulpi_dir_q <= 1'b0; |
else |
ulpi_dir_q <= ulpi_dir_i; |
|
wire turnaround_w = ulpi_dir_q ^ ulpi_dir_i; |
|
//----------------------------------------------------------------- |
// Rx - Tx delay |
//----------------------------------------------------------------- |
localparam TX_DELAY_W = 3; |
localparam TX_START_DELAY = 3'd7; |
|
reg [TX_DELAY_W-1:0] tx_delay_q; |
|
always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) |
if (ulpi_rst_i) |
tx_delay_q <= {TX_DELAY_W{1'b0}}; |
else if (utmi_rxactive_o) |
tx_delay_q <= TX_START_DELAY; |
else if (tx_delay_q != {TX_DELAY_W{1'b0}}) |
tx_delay_q <= tx_delay_q - 1; |
|
wire tx_delay_complete_w = (tx_delay_q == {TX_DELAY_W{1'b0}}); |
|
//----------------------------------------------------------------- |
// Tx Buffer - decouple UTMI Tx from PHY I/O |
//----------------------------------------------------------------- |
reg [7:0] tx_buffer_q[0:1]; |
reg tx_valid_q[0:1]; |
reg tx_wr_idx_q; |
reg tx_rd_idx_q; |
|
wire utmi_tx_ready_w; |
wire utmi_tx_accept_w; |
|
always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) |
if (ulpi_rst_i) |
begin |
tx_buffer_q[0] <= 8'b0; |
tx_buffer_q[1] <= 8'b0; |
tx_valid_q[0] <= 1'b0; |
tx_valid_q[1] <= 1'b0; |
tx_wr_idx_q <= 1'b0; |
tx_rd_idx_q <= 1'b0; |
end |
else |
begin |
// Push |
if (utmi_txvalid_i && utmi_txready_o) |
begin |
tx_buffer_q[tx_wr_idx_q] <= utmi_data_i; |
tx_valid_q[tx_wr_idx_q] <= 1'b1; |
|
tx_wr_idx_q <= tx_wr_idx_q + 1'b1; |
end |
|
// Pop |
if (utmi_tx_ready_w && utmi_tx_accept_w) |
begin |
tx_valid_q[tx_rd_idx_q] <= 1'b0; |
tx_rd_idx_q <= tx_rd_idx_q + 1'b1; |
end |
end |
|
// Tx buffer space (only accept after Rx->Tx turnaround delay) |
assign utmi_txready_o = ~tx_valid_q[tx_wr_idx_q] & tx_delay_complete_w; |
|
assign utmi_tx_ready_w = tx_valid_q[tx_rd_idx_q]; |
|
wire [7:0] utmi_tx_data_w = tx_buffer_q[tx_rd_idx_q]; |
|
//----------------------------------------------------------------- |
// Implementation |
//----------------------------------------------------------------- |
|
// Xilinx placement pragmas: |
//synthesis attribute IOB of ulpi_data_q is "TRUE" |
//synthesis attribute IOB of ulpi_stp_q is "TRUE" |
|
reg [7:0] ulpi_data_q; |
reg ulpi_stp_q; |
reg [7:0] data_q; |
|
reg utmi_rxvalid_q; |
reg utmi_rxerror_q; |
reg utmi_rxactive_q; |
reg [1:0] utmi_linestate_q; |
reg [7:0] utmi_data_q; |
|
reg cmd_wr_q; |
|
localparam REG_FUNC_CTRL = 8'h84; |
localparam REG_OTG_CTRL = 8'h8a; |
localparam REG_TRANSMIT = 8'h40; |
localparam REG_WRITE = 8'h80; |
localparam REG_READ = 8'hC0; |
|
always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) |
begin |
if (ulpi_rst_i) |
begin |
state_q <= STATE_IDLE; |
ulpi_data_q <= 8'b0; |
data_q <= 8'b0; |
ulpi_stp_q <= 1'b0; |
|
utmi_rxvalid_q <= 1'b0; |
utmi_rxerror_q <= 1'b0; |
utmi_rxactive_q <= 1'b0; |
utmi_linestate_q <= 2'b0; |
utmi_data_q <= 8'b0; |
cmd_wr_q <= 1'b0; |
end |
else |
begin |
ulpi_stp_q <= 1'b0; |
utmi_rxvalid_q <= 1'b0; |
|
if (!turnaround_w) |
begin |
//----------------------------------------------------------------- |
// Input |
//----------------------------------------------------------------- |
if (ulpi_dir_i) |
begin |
utmi_rxvalid_q <= ulpi_nxt_i; |
utmi_data_q <= ulpi_data_i; |
|
// No valid data, extract phy status |
if (!ulpi_nxt_i) |
begin |
utmi_linestate_q <= ulpi_data_i[1:0]; |
|
case (ulpi_data_i[5:4]) |
2'b00: |
begin |
utmi_rxactive_q <= 1'b0; |
utmi_rxerror_q <= 1'b0; |
end |
2'b01: |
begin |
utmi_rxactive_q <= 1'b1; |
utmi_rxerror_q <= 1'b0; |
end |
2'b11: |
begin |
utmi_rxactive_q <= 1'b1; |
utmi_rxerror_q <= 1'b1; |
end |
default: |
; // HOST_DISCONNECTED |
endcase |
end |
// RxValid (so force RxActive) |
else |
utmi_rxactive_q <= 1'b1; |
end |
//----------------------------------------------------------------- |
// Output |
//----------------------------------------------------------------- |
else |
begin |
// IDLE: Pending mode update |
if ((state_q == STATE_IDLE) && mode_update_q) |
begin |
data_q <= {1'b0, 1'b1, phy_reset_q, opmode_q, termselect_q, xcvrselect_q}; |
ulpi_data_q <= REG_FUNC_CTRL; |
|
state_q <= STATE_CMD; |
cmd_wr_q <= 1'b1; |
end |
// IDLE: Pending OTG control update |
else if ((state_q == STATE_IDLE) && otg_update_q) |
begin |
data_q <= {5'b0, dmpulldown_q, dppulldown_q, 1'b0}; |
ulpi_data_q <= REG_OTG_CTRL; |
|
state_q <= STATE_CMD; |
cmd_wr_q <= 1'b1; |
end |
// IDLE: Pending transmit |
else if ((state_q == STATE_IDLE) && utmi_tx_ready_w) |
begin |
ulpi_data_q <= REG_TRANSMIT | {4'b0, utmi_tx_data_w[3:0]}; |
state_q <= STATE_DATA; |
cmd_wr_q <= 1'b0; |
end |
// Command |
else if ((state_q == STATE_CMD) && ulpi_nxt_i) |
begin |
state_q <= STATE_DATA; |
ulpi_data_q <= data_q; |
end |
// Data |
else if (state_q == STATE_DATA && ulpi_nxt_i) |
begin |
// End of packet |
if (!utmi_tx_ready_w || cmd_wr_q) |
begin |
state_q <= STATE_IDLE; |
ulpi_data_q <= 8'b0; // IDLE |
ulpi_stp_q <= 1'b1; |
cmd_wr_q <= 1'b0; |
end |
else |
begin |
state_q <= STATE_DATA; |
ulpi_data_q <= utmi_tx_data_w; |
end |
end |
end |
end |
end |
end |
|
// Accept from buffer |
assign utmi_tx_accept_w = ((state_q == STATE_IDLE) && !(mode_update_q || otg_update_q || turnaround_w) && !ulpi_dir_i) || |
(state_q == STATE_DATA && ulpi_nxt_i && !ulpi_dir_i && !cmd_wr_q); |
|
//----------------------------------------------------------------- |
// Assignments |
//----------------------------------------------------------------- |
// ULPI Interface |
assign ulpi_data_o = ulpi_data_q; |
assign ulpi_stp_o = ulpi_stp_q; |
|
// UTMI Interface |
assign utmi_linestate_o = utmi_linestate_q; |
assign utmi_data_o = utmi_data_q; |
assign utmi_rxerror_o = utmi_rxerror_q; |
assign utmi_rxactive_o = utmi_rxactive_q; |
assign utmi_rxvalid_o = utmi_rxvalid_q; |
|
endmodule |
/trunk/README.md
0,0 → 1,27
### ULPI Link Wrapper |
|
This IP core converts from the UTMI interface to the reduced pin-count ULPI interface. |
This enables interfacing from a standard USB SIE with UTMI interface to a USB 2.0 PHY. |
|
This enables support of USB LS (1.5mbps), FS (12mbps) and HS (480mbps) transfers. |
|
The design does not support low power mode. |
|
All IOs are synchronous to the 60MHz ULPI clock input (sourced from the PHY), so care needs to be taken to configure the FPGA constraints to ensure the ULPI interface correctly meets timing. |
|
##### References |
|
* [UTMI+ Low Pin Interface (ULPI) Specification](https://www.sparkfun.com/datasheets/Components/SMD/ULPI_v1_1.pdf) |
* [SMSC USB3300 USB PHY Datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/3300db.pdf) |
|
##### Testing |
|
Verified under simulation and also on a Xilinx FPGA connected to a SMSC/Microchip USB3300 in device mode using the [USB3300 USB HS](http://www.waveshare.com/usb3300-usb-hs-board.htm) evaluation board. |
|
The supplied trivial testbench works with the free version of Modelsim. |
|
##### Size / Performance |
|
With the current configuration... |
|
* the design contains 67 flops, uses 46 slices (59 LUTs on a Xilinx Spartan 6 with IOB packing for the outputs). |