OpenCores
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).

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.