diff --git a/ulpi_wrapper/README.md b/ulpi_wrapper/README.md index b37544d..391113a 100644 --- a/ulpi_wrapper/README.md +++ b/ulpi_wrapper/README.md @@ -18,10 +18,11 @@ All IOs are synchronous to the 60MHz ULPI clock input (sourced from the PHY), so 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. +The supplied testbench requires the SystemC libraries and Icarus Verilog, both of which are available for free. ##### 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). \ No newline at end of file +* This design consumes around 88 LUTs on a Xilinx Spartan 6 with IOB packing for the outputs. +* There are around 90 flops in the design. diff --git a/ulpi_wrapper/rtl/ulpi_wrapper.v b/ulpi_wrapper/rtl/ulpi_wrapper.v index 0bd4319..082040f 100644 --- a/ulpi_wrapper/rtl/ulpi_wrapper.v +++ b/ulpi_wrapper/rtl/ulpi_wrapper.v @@ -48,6 +48,15 @@ module ulpi_wrapper input ulpi_nxt_i, output ulpi_stp_o, + // Register access (Wishbone pipelined access type) + // NOTE: Tie inputs to 0 if unused + input [7:0] reg_addr_i, + input reg_stb_i, + input reg_we_i, + input [7:0] reg_data_i, + output [7:0] reg_data_o, + output reg_ack_o, + // UTMI Interface (SIE) input utmi_txvalid_i, output utmi_txready_o, @@ -71,10 +80,19 @@ localparam STATE_W = 2; localparam STATE_IDLE = 2'd0; localparam STATE_CMD = 2'd1; localparam STATE_DATA = 2'd2; -localparam STATE_WAIT = 2'd3; +localparam STATE_REG = 2'd3; reg [STATE_W-1:0] state_q; +//----------------------------------------------------------------- +// Local Params +//----------------------------------------------------------------- +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; + //----------------------------------------------------------------- // UTMI Mode Select //----------------------------------------------------------------- @@ -83,6 +101,8 @@ reg [1:0] xcvrselect_q; reg termselect_q; reg [1:0] opmode_q; reg phy_reset_q; +reg auto_wr_q; +reg reg_wr_q; always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) if (ulpi_rst_i) @@ -99,7 +119,7 @@ begin termselect_q <= utmi_termselect_i; opmode_q <= utmi_opmode_i; - if (mode_update_q && (state_q == STATE_IDLE) && !ulpi_dir_i) + if (mode_update_q && (state_q == STATE_CMD) && (ulpi_data_o == REG_FUNC_CTRL)) begin mode_update_q <= 1'b0; phy_reset_q <= 1'b0; @@ -129,7 +149,7 @@ 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) + if (otg_update_q && (state_q == STATE_CMD) && (ulpi_data_o == REG_OTG_CTRL)) otg_update_q <= 1'b0; else if (dppulldown_q != utmi_dppulldown_i || dmpulldown_q != utmi_dmpulldown_i) @@ -149,6 +169,64 @@ else wire turnaround_w = ulpi_dir_q ^ ulpi_dir_i; +reg ulpi_rxcmd_q; +always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) +if (ulpi_rst_i) + ulpi_rxcmd_q <= 1'b0; +// Switch to input with NXT asserted in turnaround cycle +else if (!ulpi_dir_q && ulpi_dir_i && ulpi_nxt_i) + ulpi_rxcmd_q <= 1'b1; +// Switch to output (turnaround cycle) +else if (ulpi_dir_q && !ulpi_dir_i) + ulpi_rxcmd_q <= 1'b0; + +//----------------------------------------------------------------- +// Register Access +//----------------------------------------------------------------- +reg reg_wr_pending_q; +reg reg_rd_pending_q; +reg [7:0] reg_addr_q; +reg [7:0] reg_data_q; +reg reg_ack_q; + +wire reg_ready_w = (reg_wr_pending_q && state_q == STATE_REG && ulpi_nxt_i && reg_wr_q) || + (reg_rd_pending_q && !turnaround_w && ulpi_dir_i && !ulpi_rxcmd_q); + +always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) +if (ulpi_rst_i) +begin + reg_wr_pending_q <= 1'b0; + reg_rd_pending_q <= 1'b0; + reg_addr_q <= 8'b0; +end +else if (reg_stb_i) +begin + reg_addr_q <= reg_addr_i; + reg_wr_pending_q <= reg_we_i; + reg_rd_pending_q <= ~reg_we_i; +end +else if (reg_ready_w) +begin + reg_wr_pending_q <= 1'b0; + reg_rd_pending_q <= 1'b0; +end + +always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) +if (ulpi_rst_i) + reg_data_q <= 8'b0; +else if (reg_stb_i && reg_we_i) + reg_data_q <= reg_data_i; + +assign reg_data_o = utmi_data_o; + +always @ (posedge ulpi_clk60_i or posedge ulpi_rst_i) +if (ulpi_rst_i) + reg_ack_q <= 1'b0; +else + reg_ack_q <= reg_ready_w; + +assign reg_ack_o = reg_ack_q; + //----------------------------------------------------------------- // Rx - Tx delay //----------------------------------------------------------------- @@ -232,14 +310,6 @@ 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) @@ -254,7 +324,8 @@ begin utmi_rxactive_q <= 1'b0; utmi_linestate_q <= 2'b0; utmi_data_q <= 8'b0; - cmd_wr_q <= 1'b0; + auto_wr_q <= 1'b0; + reg_wr_q <= 1'b0; end else begin @@ -264,9 +335,9 @@ begin if (!turnaround_w) begin //----------------------------------------------------------------- - // Input + // Input: RX_DATA //----------------------------------------------------------------- - if (ulpi_dir_i) + if (ulpi_dir_i && ulpi_rxcmd_q) begin utmi_rxvalid_q <= ulpi_nxt_i; utmi_data_q <= ulpi_data_i; @@ -301,6 +372,14 @@ begin utmi_rxactive_q <= 1'b1; end //----------------------------------------------------------------- + // Input: REG_DATA + //----------------------------------------------------------------- + else if (ulpi_dir_i) + begin + utmi_rxvalid_q <= 1'b0; + utmi_data_q <= ulpi_data_i; + end + //----------------------------------------------------------------- // Output //----------------------------------------------------------------- else @@ -312,7 +391,8 @@ begin ulpi_data_q <= REG_FUNC_CTRL; state_q <= STATE_CMD; - cmd_wr_q <= 1'b1; + auto_wr_q <= 1'b1; + reg_wr_q <= 1'b0; end // IDLE: Pending OTG control update else if ((state_q == STATE_IDLE) && otg_update_q) @@ -321,31 +401,65 @@ begin ulpi_data_q <= REG_OTG_CTRL; state_q <= STATE_CMD; - cmd_wr_q <= 1'b1; + auto_wr_q <= 1'b1; + reg_wr_q <= 1'b0; end + // IDLE: Pending register access + else if ((state_q == STATE_IDLE) && (reg_wr_pending_q || reg_rd_pending_q)) + begin + data_q <= reg_data_q; + + if (reg_wr_pending_q) + ulpi_data_q <= REG_WRITE | {2'b0, reg_addr_q[5:0]}; + else + ulpi_data_q <= REG_READ | {2'b0, reg_addr_q[5:0]}; + + state_q <= STATE_CMD; + auto_wr_q <= 1'b0; + reg_wr_q <= reg_wr_pending_q; + 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; + auto_wr_q <= 1'b0; + reg_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; + // Read Register + if (!reg_wr_q && !auto_wr_q) + begin + state_q <= STATE_IDLE; + ulpi_data_q <= 8'b0; + end + // Write Register + else + begin + state_q <= STATE_REG; + ulpi_data_q <= data_q; + end + end + // Data (register write) + else if (state_q == STATE_REG && ulpi_nxt_i) + begin + state_q <= STATE_IDLE; + ulpi_data_q <= 8'b0; // IDLE + ulpi_stp_q <= 1'b1; + auto_wr_q <= 1'b0; + reg_wr_q <= 1'b0; end // Data else if (state_q == STATE_DATA && ulpi_nxt_i) begin // End of packet - if (!utmi_tx_ready_w || cmd_wr_q) + if (!utmi_tx_ready_w) begin state_q <= STATE_IDLE; ulpi_data_q <= 8'b0; // IDLE ulpi_stp_q <= 1'b1; - cmd_wr_q <= 1'b0; end else begin @@ -359,8 +473,8 @@ begin 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); +assign utmi_tx_accept_w = ((state_q == STATE_IDLE) && !(mode_update_q || otg_update_q || turnaround_w || reg_wr_pending_q || reg_rd_pending_q) && !ulpi_dir_i) || + (state_q == STATE_DATA && ulpi_nxt_i && !ulpi_dir_i); //----------------------------------------------------------------- // Assignments diff --git a/ulpi_wrapper/testbench/gtksettings.sav b/ulpi_wrapper/testbench/gtksettings.sav deleted file mode 100644 index 6218e50..0000000 --- a/ulpi_wrapper/testbench/gtksettings.sav +++ /dev/null @@ -1,57 +0,0 @@ -@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 diff --git a/ulpi_wrapper/testbench/makefile b/ulpi_wrapper/testbench/makefile index 0934525..fb3991c 100644 --- a/ulpi_wrapper/testbench/makefile +++ b/ulpi_wrapper/testbench/makefile @@ -1,35 +1,58 @@ -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 +######################################################### +# Vars +######################################################### +SYSTEMC_HOME ?= /opt/systemc-2.3.1 + +TRACE ?= 1 + +DUT_NAME = ulpi_wrapper +RTL_DUT = ../rtl/ulpi_wrapper.v + +######################################################### +# Source +######################################################### +SRC = $(filter-out $(DUT_NAME).cpp,$(wildcard *.cpp)) +SRC += $(DUT_NAME).cpp + +SRC_V = tb_top.v +SRC_V += $(RTL_DUT) + +OBJ = $(patsubst %.cpp,%.o,$(SRC)) + +VPI_OBJ = dut + +######################################################### +# CFLAGS +######################################################### +INC_PATH = -I. +INC_PATH += -I/usr/include/iverilog +INC_PATH += -I$(SYSTEMC_HOME)/include + +VINC_PATH = -I. -I../rtl +VFLAGS = -DTRACE=$(TRACE) + +CFLAGS = -c -fpic + +LIB_OPT = $(SYSTEMC_HOME)/lib-linux64/libsystemc.a + +EXE = output.out + +######################################################### +# Rules +######################################################### +all: run + +%.o : %.cpp + gcc -c $(INC_PATH) $(CFLAGS) $< -o $@ + +$(VPI_OBJ).vpi: $(OBJ) + g++ -shared -o $(VPI_OBJ).vpi -Wl,--whole-archive $(LIB_OPT) $(OBJ) -Wl,--no-whole-archive + +$(EXE) : $(SRC_V) + iverilog -o $(EXE) $(SRC_V) $(VINC_PATH) $(VFLAGS) + +run: $(EXE) $(VPI_OBJ).vpi + vvp -M. -m$(VPI_OBJ) $(EXE) -vcd + +clean: + rm -rf $(OBJ) dut.vpi *.vcd *.out \ No newline at end of file diff --git a/ulpi_wrapper/testbench/simulation.svh b/ulpi_wrapper/testbench/simulation.svh deleted file mode 100644 index 89a2361..0000000 --- a/ulpi_wrapper/testbench/simulation.svh +++ /dev/null @@ -1,127 +0,0 @@ -`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; \ No newline at end of file diff --git a/ulpi_wrapper/testbench/tb_top.v b/ulpi_wrapper/testbench/tb_top.v new file mode 100644 index 0000000..56f673e --- /dev/null +++ b/ulpi_wrapper/testbench/tb_top.v @@ -0,0 +1,96 @@ +`timescale 1ns / 1ns + +//----------------------------------------------------------------- +// Module: Auto generated top +//----------------------------------------------------------------- +module tb_top(); + +reg ulpi_clk60_i; +reg ulpi_rst_i; +reg [7:0] ulpi_data_i; +wire [7:0] ulpi_data_o; +reg ulpi_dir_i; +reg ulpi_nxt_i; +wire ulpi_stp_o; +reg [7:0] reg_addr_i; +reg reg_stb_i; +reg reg_we_i; +reg [7:0] reg_data_i; +wire [7:0] reg_data_o; +wire reg_ack_o; +reg utmi_txvalid_i; +wire utmi_txready_o; +wire utmi_rxvalid_o; +wire utmi_rxactive_o; +wire utmi_rxerror_o; +wire [7:0] utmi_data_o; +reg [7:0] utmi_data_i; +reg [1:0] utmi_xcvrselect_i; +reg utmi_termselect_i; +reg [1:0] utmi_opmode_i; +reg utmi_dppulldown_i; +reg utmi_dmpulldown_i; +wire [1:0] utmi_linestate_o; + +//----------------------------------------------------------------- +// DUT +//----------------------------------------------------------------- +ulpi_wrapper dut +( + .ulpi_clk60_i(ulpi_clk60_i) + , .ulpi_rst_i(ulpi_rst_i) + , .ulpi_data_i(ulpi_data_i) + , .ulpi_data_o(ulpi_data_o) + , .ulpi_dir_i(ulpi_dir_i) + , .ulpi_nxt_i(ulpi_nxt_i) + , .ulpi_stp_o(ulpi_stp_o) + , .reg_addr_i(reg_addr_i) + , .reg_stb_i(reg_stb_i) + , .reg_we_i(reg_we_i) + , .reg_data_i(reg_data_i) + , .reg_data_o(reg_data_o) + , .reg_ack_o(reg_ack_o) + , .utmi_txvalid_i(utmi_txvalid_i) + , .utmi_txready_o(utmi_txready_o) + , .utmi_rxvalid_o(utmi_rxvalid_o) + , .utmi_rxactive_o(utmi_rxactive_o) + , .utmi_rxerror_o(utmi_rxerror_o) + , .utmi_data_o(utmi_data_o) + , .utmi_data_i(utmi_data_i) + , .utmi_xcvrselect_i(utmi_xcvrselect_i) + , .utmi_termselect_i(utmi_termselect_i) + , .utmi_opmode_i(utmi_opmode_i) + , .utmi_dppulldown_i(utmi_dppulldown_i) + , .utmi_dmpulldown_i(utmi_dmpulldown_i) + , .utmi_linestate_o(utmi_linestate_o) +); + +//----------------------------------------------------------------- +// Reset +//----------------------------------------------------------------- +initial +begin + ulpi_clk60_i = 1; + ulpi_rst_i = 1; + + // Hookup System C + $attach_system_c(); + + if (`TRACE) + begin + $dumpfile("waveform.vcd"); + $dumpvars(0,tb_top); + end + +#51 ulpi_rst_i = 0; +end + +//----------------------------------------------------------------- +// Clock +//----------------------------------------------------------------- +always +begin + #5 ulpi_clk60_i = !ulpi_clk60_i; +end + +endmodule \ No newline at end of file diff --git a/ulpi_wrapper/testbench/top_tb.sv b/ulpi_wrapper/testbench/top_tb.sv deleted file mode 100644 index b014b01..0000000 --- a/ulpi_wrapper/testbench/top_tb.sv +++ /dev/null @@ -1,299 +0,0 @@ -`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 diff --git a/ulpi_wrapper/testbench/ulpi_driver.cpp b/ulpi_wrapper/testbench/ulpi_driver.cpp new file mode 100644 index 0000000..80d1381 --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_driver.cpp @@ -0,0 +1,270 @@ +#include "ulpi_driver.h" + +#define CMD_IDLE 0x0 +#define CMD_XMIT 0x1 +#define CMD_REG_WR 0x2 +#define CMD_REG_RD 0x3 + +#define ULPI_CMD_H 7 +#define ULPI_CMD_L 6 + +#define ULPI_ADDR_H 5 +#define ULPI_ADDR_L 0 + +#define ULPI_PID_H 3 +#define ULPI_PID_L 0 + +//----------------------------------------------------------------- +// reg_write +//----------------------------------------------------------------- +void ulpi_driver::reg_write(sc_uint <8> addr, sc_uint <8> data) +{ + m_reg[addr & (ULPI_REG_NUM-1)] = data; +} +//----------------------------------------------------------------- +// reg_read +//----------------------------------------------------------------- +sc_uint <8> ulpi_driver::reg_read(sc_uint <8> addr) +{ + return m_reg[addr & (ULPI_REG_NUM-1)]; +} +//----------------------------------------------------------------- +// drive_rxcmd: Drive ULPI RX_CMD +//----------------------------------------------------------------- +void ulpi_driver::drive_rxcmd(sc_uint <2> linestate, bool rx_active, bool rx_error) +{ + sc_uint<8> data = 0; + + data.range(ULPI_RXCMD_LS_H, ULPI_RXCMD_LS_L) = linestate; + + if (rx_error) + data.range(ULPI_RXCMD_RXEVENT_H, ULPI_RXCMD_RXEVENT_L) = ULPI_RXEVENT_ERROR; + else if (rx_active) + data.range(ULPI_RXCMD_RXEVENT_H, ULPI_RXCMD_RXEVENT_L) = ULPI_RXEVENT_ACTIVE; + else + data.range(ULPI_RXCMD_RXEVENT_H, ULPI_RXCMD_RXEVENT_L) = ULPI_RXEVENT_INACTIVE; + + // RX_CMD + ulpi_dir_o.write(true); + ulpi_nxt_o.write(false); + ulpi_data_o.write(data); + + wait(); +} +//----------------------------------------------------------------- +// drive_rxdata: Drive ULPI RX_DATA +//----------------------------------------------------------------- +void ulpi_driver::drive_rxdata(sc_uint <8> data) +{ + // RX_DATA + ulpi_dir_o.write(true); + ulpi_nxt_o.write(true); + ulpi_data_o.write(data); + + wait(); +} +//----------------------------------------------------------------- +// drive_output: Drive turnaround cycle (-> output) +//----------------------------------------------------------------- +void ulpi_driver::drive_output(bool rx_data) +{ + // Turnaround + ulpi_dir_o.write(true); + ulpi_nxt_o.write(rx_data); + ulpi_data_o.write(0x00); + wait(); + ulpi_nxt_o.write(false); +} +//----------------------------------------------------------------- +// drive_input: Drive turnaround cycle (-> input) +//----------------------------------------------------------------- +void ulpi_driver::drive_input(void) +{ + // Turnaround + ulpi_dir_o.write(false); + ulpi_nxt_o.write(false); + ulpi_data_o.write(0x00); + + wait(); +} +//----------------------------------------------------------------- +// drive +//----------------------------------------------------------------- +void ulpi_driver::drive(void) +{ + drive_input(); + + // Wait until reset complete + while (rst_i.read()) + wait(); + + while (true) + { + // PHY -> LINK + if (m_tx_fifo.num_available()) + { + sc_uint <9> fifo_data; + sc_uint <8> data; + bool last; + + // Turnaround + drive_output(true); + + do + { + // RX_CMD + if (!(rand() % 4)) + { + last = false; + + // RX_CMD (RX_ACTIVE = 1) + drive_rxcmd(0x2, true, false); + } + // RX_DATA + else + { + last = tx_read(data); + drive_rxdata(data); + } + } + while (!last); + + // RX_CMD (RX_ACTIVE = 0) + drive_rxcmd(0x2, false, false); + + // Turnaround + drive_input(); + } + // LINK -> PHY + else + { + sc_uint <8> data = ulpi_data_i.read(); + sc_uint <2> cmd = data.range(ULPI_CMD_H,ULPI_CMD_L); + sc_uint <6> addr = data.range(ULPI_ADDR_H,ULPI_ADDR_L); + sc_uint <8> pid = 0; + + pid.range(3,0) = data.range(ULPI_PID_H,ULPI_PID_L); + pid.range(7,4) = ~data.range(ULPI_PID_H,ULPI_PID_L); + + // Register read + if (cmd == CMD_REG_RD) + { + // Accept command + ulpi_nxt_o.write(true); + wait(); + + // Turnaround + drive_output(false); + + // Data + data = reg_read(addr); + ulpi_data_o.write(data); + wait(); + + // Turnaround + drive_input(); + } + // Not idle? + else if (cmd != CMD_IDLE) + { + // Accept command + ulpi_nxt_o.write(true); + wait(); + ulpi_nxt_o.write(false); + + // Record PID for future use + bool last_valid = (cmd == CMD_XMIT); + sc_uint <8> last_data = pid; + + while (!ulpi_stp_i.read()) + { + // Random data accept delay + if (!(rand() % 4)) + { + int wait_len = rand() % 8; + + ulpi_nxt_o.write(false); + while (!ulpi_stp_i.read() && wait_len--) + wait(1); + + if (ulpi_stp_i.read()) + break; + } + + ulpi_nxt_o.write(true); + wait(); + ulpi_nxt_o.write(false); + + if (ulpi_stp_i.read()) + break; + + sc_uint <8> data = ulpi_data_i.read(); + + // Transmit + if (cmd == CMD_XMIT) + { + if (last_valid) + rx_write(last_data, false); + + last_valid = true; + last_data = data; + } + // Register write + else if (cmd == CMD_REG_WR) + { + reg_write(addr, data); + addr += 1; + } + } + + // Flush pending received byte + if (last_valid) + rx_write(last_data, true); + } + + wait(); + } + } +} +//----------------------------------------------------------------- +// write +//----------------------------------------------------------------- +void ulpi_driver::write(sc_uint <8> data, bool last) +{ + sc_uint <9> fifo_data; + + fifo_data.range(7,0) = data; + fifo_data.range(8,8) = last; + + m_tx_fifo.write(fifo_data); +} +//----------------------------------------------------------------- +// read +//----------------------------------------------------------------- +bool ulpi_driver::read(sc_uint <8> &data) +{ + sc_uint <9> fifo_data = m_rx_fifo.read(); + data = fifo_data.range(7,0); + return (bool)fifo_data.range(8,8); +} +//----------------------------------------------------------------- +// rx_write +//----------------------------------------------------------------- +void ulpi_driver::rx_write(sc_uint <8> data, bool last) +{ + sc_uint <9> fifo_data; + + fifo_data.range(7,0) = data; + fifo_data.range(8,8) = last; + + m_rx_fifo.write(fifo_data); +} +//----------------------------------------------------------------- +// tx_read +//----------------------------------------------------------------- +bool ulpi_driver::tx_read(sc_uint <8> &data) +{ + sc_uint <9> fifo_data = m_tx_fifo.read(); + data = fifo_data.range(7,0); + return (bool)fifo_data.range(8,8); +} diff --git a/ulpi_wrapper/testbench/ulpi_driver.h b/ulpi_wrapper/testbench/ulpi_driver.h new file mode 100644 index 0000000..e39fd25 --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_driver.h @@ -0,0 +1,117 @@ +#ifndef ULPI_DRIVER_H +#define ULPI_DRIVER_H + +#include + +//----------------------------------------------------------------- +// Defines +//----------------------------------------------------------------- +#define ULPI_REG_VIDL 0x0 +#define ULPI_REG_VIDH 0x1 +#define ULPI_REG_PIDL 0x2 +#define ULPI_REG_PIDH 0x3 +#define ULPI_REG_FUNC 0x4 +#define ULPI_REG_OTG 0xa +#define ULPI_REG_SCRATCH 0x16 +#define ULPI_REG_NUM 0x20 + +#define ULPI_RXCMD_LS_L 0 +#define ULPI_RXCMD_LS_H 1 +#define ULPI_RXCMD_RXEVENT_L 4 +#define ULPI_RXCMD_RXEVENT_H 5 +#define ULPI_RXEVENT_INACTIVE 0 +#define ULPI_RXEVENT_ACTIVE 1 +#define ULPI_RXEVENT_HOSTDC 2 +#define ULPI_RXEVENT_ERROR 3 + +//----------------------------------------------------------------- +// ulpi_driver: ULPI Master Driver (PHY TB component) +//----------------------------------------------------------------- +SC_MODULE (ulpi_driver) +{ +public: + //------------------------------------------------------------- + // Interface I/O + //------------------------------------------------------------- + // Clock and Reset + sc_in clk_i; + sc_in rst_i; + + // I/O + sc_out > ulpi_data_o; + sc_in > ulpi_data_i; + sc_out ulpi_dir_o; + sc_out ulpi_nxt_o; + sc_in ulpi_stp_i; + + //------------------------------------------------------------- + // Constructor + //------------------------------------------------------------- + SC_HAS_PROCESS(ulpi_driver); + ulpi_driver(sc_module_name name): sc_module(name), + m_tx_fifo(1024), + m_rx_fifo(1024) + { + SC_CTHREAD(drive, clk_i.pos()); + + m_reg[ULPI_REG_VIDL] = 0x24; + m_reg[ULPI_REG_VIDH] = 0x04; + m_reg[ULPI_REG_PIDL] = 0x04; + m_reg[ULPI_REG_PIDH] = 0x00; + m_reg[ULPI_REG_FUNC] = 0x41; + m_reg[ULPI_REG_OTG] = 0x06; + m_reg[ULPI_REG_SCRATCH] = 0x00; + } + + //------------------------------------------------------------- + // Trace + //------------------------------------------------------------- + void add_trace(sc_trace_file *vcd, std::string prefix) + { + #undef TRACE_SIGNAL + #define TRACE_SIGNAL(s) sc_trace(vcd,s,prefix + #s) + + TRACE_SIGNAL(ulpi_data_o); + TRACE_SIGNAL(ulpi_data_i); + TRACE_SIGNAL(ulpi_dir_o); + TRACE_SIGNAL(ulpi_nxt_o); + TRACE_SIGNAL(ulpi_stp_i); + + #undef TRACE_SIGNAL + } + + //------------------------------------------------------------- + // API + //------------------------------------------------------------- +public: + void write(sc_uint <8> data, bool last = false); + bool read(sc_uint <8> &data); + + bool write_empty(void) { return m_tx_fifo.num_available() == 0; } + bool read_ready(void) { return m_rx_fifo.num_available() > 0; } + + //------------------------------------------------------------- + // Internal + //------------------------------------------------------------- +protected: + void drive(void); + void rx_write(sc_uint <8> data, bool last); + bool tx_read(sc_uint <8> &data); + + void drive_rxcmd(sc_uint <2> linestate, bool rx_active, bool rx_error); + void drive_rxdata(sc_uint <8> data); + void drive_output(bool rx_data); + void drive_input(void); + + void reg_write(sc_uint <8> addr, sc_uint <8> data); + sc_uint <8> reg_read(sc_uint <8> addr); + + //------------------------------------------------------------- + // Members + //------------------------------------------------------------- + sc_fifo < sc_uint<9> > m_tx_fifo; + sc_fifo < sc_uint<9> > m_rx_fifo; + sc_uint <8> m_reg[ULPI_REG_NUM]; +}; + +#endif \ No newline at end of file diff --git a/ulpi_wrapper/testbench/ulpi_utmi.v b/ulpi_wrapper/testbench/ulpi_utmi.v deleted file mode 100644 index 9836d28..0000000 --- a/ulpi_wrapper/testbench/ulpi_utmi.v +++ /dev/null @@ -1,397 +0,0 @@ -//----------------------------------------------------------------- -// 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 diff --git a/ulpi_wrapper/testbench/ulpi_wrapper.cpp b/ulpi_wrapper/testbench/ulpi_wrapper.cpp new file mode 100644 index 0000000..378b7c4 --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_wrapper.cpp @@ -0,0 +1,253 @@ +#include +#include +#include +#include + +#include "ulpi_wrapper.h" + +// User provided init function +extern void sc_main_tb(void); + +//----------------------------------------------------------------- +// valueChangeCb +//----------------------------------------------------------------- +int ulpi_wrapper::valueChangeCb(void) +{ + s_vpi_value value_s; + static uint64_t last_time = 0; + + s_vpi_time vpi_time_s; + + vpi_time_s.type = vpiSimTime; + vpi_time_s.high = 0; + vpi_time_s.low = 0; + + // For each I/O + vpiHandle handle_ulpi_clk60_i = vpi_handle_by_name("tb_top.ulpi_clk60_i", NULL); + vpiHandle handle_ulpi_rst_i = vpi_handle_by_name("tb_top.ulpi_rst_i", NULL); + vpiHandle handle_ulpi_data_i = vpi_handle_by_name("tb_top.ulpi_data_i", NULL); + vpiHandle handle_ulpi_data_o = vpi_handle_by_name("tb_top.ulpi_data_o", NULL); + vpiHandle handle_ulpi_dir_i = vpi_handle_by_name("tb_top.ulpi_dir_i", NULL); + vpiHandle handle_ulpi_nxt_i = vpi_handle_by_name("tb_top.ulpi_nxt_i", NULL); + vpiHandle handle_ulpi_stp_o = vpi_handle_by_name("tb_top.ulpi_stp_o", NULL); + vpiHandle handle_reg_addr_i = vpi_handle_by_name("tb_top.reg_addr_i", NULL); + vpiHandle handle_reg_stb_i = vpi_handle_by_name("tb_top.reg_stb_i", NULL); + vpiHandle handle_reg_we_i = vpi_handle_by_name("tb_top.reg_we_i", NULL); + vpiHandle handle_reg_data_i = vpi_handle_by_name("tb_top.reg_data_i", NULL); + vpiHandle handle_reg_data_o = vpi_handle_by_name("tb_top.reg_data_o", NULL); + vpiHandle handle_reg_ack_o = vpi_handle_by_name("tb_top.reg_ack_o", NULL); + vpiHandle handle_utmi_txvalid_i = vpi_handle_by_name("tb_top.utmi_txvalid_i", NULL); + vpiHandle handle_utmi_txready_o = vpi_handle_by_name("tb_top.utmi_txready_o", NULL); + vpiHandle handle_utmi_rxvalid_o = vpi_handle_by_name("tb_top.utmi_rxvalid_o", NULL); + vpiHandle handle_utmi_rxactive_o = vpi_handle_by_name("tb_top.utmi_rxactive_o", NULL); + vpiHandle handle_utmi_rxerror_o = vpi_handle_by_name("tb_top.utmi_rxerror_o", NULL); + vpiHandle handle_utmi_data_o = vpi_handle_by_name("tb_top.utmi_data_o", NULL); + vpiHandle handle_utmi_data_i = vpi_handle_by_name("tb_top.utmi_data_i", NULL); + vpiHandle handle_utmi_xcvrselect_i = vpi_handle_by_name("tb_top.utmi_xcvrselect_i", NULL); + vpiHandle handle_utmi_termselect_i = vpi_handle_by_name("tb_top.utmi_termselect_i", NULL); + vpiHandle handle_utmi_opmode_i = vpi_handle_by_name("tb_top.utmi_opmode_i", NULL); + vpiHandle handle_utmi_dppulldown_i = vpi_handle_by_name("tb_top.utmi_dppulldown_i", NULL); + vpiHandle handle_utmi_dmpulldown_i = vpi_handle_by_name("tb_top.utmi_dmpulldown_i", NULL); + vpiHandle handle_utmi_linestate_o = vpi_handle_by_name("tb_top.utmi_linestate_o", NULL); + + // Read current value from Verilog + value_s.format = vpiIntVal; + + // Clock & Reset + vpi_get_value(handle_ulpi_clk60_i, &value_s); + m_clk.write(value_s.value.integer); + vpi_get_value(handle_ulpi_rst_i, &value_s); + m_rst.write(value_s.value.integer); + + // Outputs + vpi_get_value(handle_ulpi_data_o, &value_s); + ulpi_data_o.write(value_s.value.integer); + vpi_get_value(handle_ulpi_stp_o, &value_s); + ulpi_stp_o.write(value_s.value.integer); + vpi_get_value(handle_reg_data_o, &value_s); + reg_data_o.write(value_s.value.integer); + vpi_get_value(handle_reg_ack_o, &value_s); + reg_ack_o.write(value_s.value.integer); + vpi_get_value(handle_utmi_txready_o, &value_s); + utmi_txready_o.write(value_s.value.integer); + vpi_get_value(handle_utmi_rxvalid_o, &value_s); + utmi_rxvalid_o.write(value_s.value.integer); + vpi_get_value(handle_utmi_rxactive_o, &value_s); + utmi_rxactive_o.write(value_s.value.integer); + vpi_get_value(handle_utmi_rxerror_o, &value_s); + utmi_rxerror_o.write(value_s.value.integer); + vpi_get_value(handle_utmi_data_o, &value_s); + utmi_data_o.write(value_s.value.integer); + vpi_get_value(handle_utmi_linestate_o, &value_s); + utmi_linestate_o.write(value_s.value.integer); + + // Get current time + uint64_t time_value = 0; + s_vpi_time time_now; + time_now.type = vpiSimTime; + vpi_get_time (0, &time_now); + + time_value = time_now.high; + time_value <<= 32; + time_value |= time_now.low; + + // Update systemC TB + if(sc_pending_activity()) + sc_start((int)(time_value-last_time),SC_NS); + + last_time = time_value; + + // Inputs + value_s.value.integer = ulpi_data_i.read(); + vpi_put_value(handle_ulpi_data_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = ulpi_dir_i.read(); + vpi_put_value(handle_ulpi_dir_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = ulpi_nxt_i.read(); + vpi_put_value(handle_ulpi_nxt_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = reg_addr_i.read(); + vpi_put_value(handle_reg_addr_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = reg_stb_i.read(); + vpi_put_value(handle_reg_stb_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = reg_we_i.read(); + vpi_put_value(handle_reg_we_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = reg_data_i.read(); + vpi_put_value(handle_reg_data_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_txvalid_i.read(); + vpi_put_value(handle_utmi_txvalid_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_data_i.read(); + vpi_put_value(handle_utmi_data_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_xcvrselect_i.read(); + vpi_put_value(handle_utmi_xcvrselect_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_termselect_i.read(); + vpi_put_value(handle_utmi_termselect_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_opmode_i.read(); + vpi_put_value(handle_utmi_opmode_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_dppulldown_i.read(); + vpi_put_value(handle_utmi_dppulldown_i, &value_s, &vpi_time_s, vpiInertialDelay); + value_s.value.integer = utmi_dmpulldown_i.read(); + vpi_put_value(handle_utmi_dmpulldown_i, &value_s, &vpi_time_s, vpiInertialDelay); + + if (isStopped()) + vpi_sim_control(vpiFinish, 0); + + return 0; +} +//----------------------------------------------------------------- +// value_change +//----------------------------------------------------------------- +static int value_change(p_cb_data cb_data) +{ + ulpi_wrapper *p = (ulpi_wrapper*)cb_data->user_data; + return p->valueChangeCb(); +} +//----------------------------------------------------------------- +// attachCb +//----------------------------------------------------------------- +int ulpi_wrapper::attachCb(void) +{ + static s_vpi_value value_s; + static s_vpi_time vpi_time; + s_cb_data cb_data_s; + + vpi_time.high = 0; + vpi_time.low = 0; + vpi_time.type = vpiSimTime; + + // For each I/O + vpiHandle handle_ulpi_clk60_i = vpi_handle_by_name("tb_top.ulpi_clk60_i", NULL); + vpiHandle handle_ulpi_rst_i = vpi_handle_by_name("tb_top.ulpi_rst_i", NULL); + vpiHandle handle_ulpi_data_i = vpi_handle_by_name("tb_top.ulpi_data_i", NULL); + vpiHandle handle_ulpi_data_o = vpi_handle_by_name("tb_top.ulpi_data_o", NULL); + vpiHandle handle_ulpi_dir_i = vpi_handle_by_name("tb_top.ulpi_dir_i", NULL); + vpiHandle handle_ulpi_nxt_i = vpi_handle_by_name("tb_top.ulpi_nxt_i", NULL); + vpiHandle handle_ulpi_stp_o = vpi_handle_by_name("tb_top.ulpi_stp_o", NULL); + vpiHandle handle_reg_addr_i = vpi_handle_by_name("tb_top.reg_addr_i", NULL); + vpiHandle handle_reg_stb_i = vpi_handle_by_name("tb_top.reg_stb_i", NULL); + vpiHandle handle_reg_we_i = vpi_handle_by_name("tb_top.reg_we_i", NULL); + vpiHandle handle_reg_data_i = vpi_handle_by_name("tb_top.reg_data_i", NULL); + vpiHandle handle_reg_data_o = vpi_handle_by_name("tb_top.reg_data_o", NULL); + vpiHandle handle_reg_ack_o = vpi_handle_by_name("tb_top.reg_ack_o", NULL); + vpiHandle handle_utmi_txvalid_i = vpi_handle_by_name("tb_top.utmi_txvalid_i", NULL); + vpiHandle handle_utmi_txready_o = vpi_handle_by_name("tb_top.utmi_txready_o", NULL); + vpiHandle handle_utmi_rxvalid_o = vpi_handle_by_name("tb_top.utmi_rxvalid_o", NULL); + vpiHandle handle_utmi_rxactive_o = vpi_handle_by_name("tb_top.utmi_rxactive_o", NULL); + vpiHandle handle_utmi_rxerror_o = vpi_handle_by_name("tb_top.utmi_rxerror_o", NULL); + vpiHandle handle_utmi_data_o = vpi_handle_by_name("tb_top.utmi_data_o", NULL); + vpiHandle handle_utmi_data_i = vpi_handle_by_name("tb_top.utmi_data_i", NULL); + vpiHandle handle_utmi_xcvrselect_i = vpi_handle_by_name("tb_top.utmi_xcvrselect_i", NULL); + vpiHandle handle_utmi_termselect_i = vpi_handle_by_name("tb_top.utmi_termselect_i", NULL); + vpiHandle handle_utmi_opmode_i = vpi_handle_by_name("tb_top.utmi_opmode_i", NULL); + vpiHandle handle_utmi_dppulldown_i = vpi_handle_by_name("tb_top.utmi_dppulldown_i", NULL); + vpiHandle handle_utmi_dmpulldown_i = vpi_handle_by_name("tb_top.utmi_dmpulldown_i", NULL); + vpiHandle handle_utmi_linestate_o = vpi_handle_by_name("tb_top.utmi_linestate_o", NULL); + + // Attach value change callbacks for outputs + cb_data_s.user_data = (PLI_BYTE8*)this; + cb_data_s.reason = cbValueChange; + cb_data_s.cb_rtn = value_change; + cb_data_s.time = &vpi_time; + cb_data_s.value = &value_s; + + value_s.format = vpiIntVal; + + cb_data_s.obj = handle_ulpi_clk60_i; + vpi_register_cb(&cb_data_s); + + cb_data_s.obj = handle_ulpi_rst_i; + vpi_register_cb(&cb_data_s); + + cb_data_s.obj = handle_ulpi_data_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_ulpi_stp_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_reg_data_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_reg_ack_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_utmi_txready_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_utmi_rxvalid_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_utmi_rxactive_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_utmi_rxerror_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_utmi_data_o; + vpi_register_cb(&cb_data_s); + cb_data_s.obj = handle_utmi_linestate_o; + vpi_register_cb(&cb_data_s); + + // Initialize SystemC Model + sc_main_tb(); + + return 0; +} +//----------------------------------------------------------------- +// attach_system_c +//----------------------------------------------------------------- +static int attach_system_c(char *user_data) +{ + ulpi_wrapper *p = (ulpi_wrapper*)user_data; + return p->attachCb(); +} +//----------------------------------------------------------------- +// _register +//----------------------------------------------------------------- +static void _register(void) +{ + s_vpi_systf_data tf_data; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$attach_system_c"; + tf_data.calltf = attach_system_c; + tf_data.compiletf = 0; + tf_data.sizetf = 0; + tf_data.user_data = (PLI_BYTE8*)&ulpi_wrapper::getInstance(); + vpi_register_systf(&tf_data); +} + +void (*vlog_startup_routines[])() = +{ + _register, + 0 +}; diff --git a/ulpi_wrapper/testbench/ulpi_wrapper.h b/ulpi_wrapper/testbench/ulpi_wrapper.h new file mode 100644 index 0000000..69ef4ea --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_wrapper.h @@ -0,0 +1,65 @@ +#ifndef ULPI_WRAPPER_H +#define ULPI_WRAPPER_H + +#include + +//----------------------------------------------------------------- +// DUT +//----------------------------------------------------------------- +SC_MODULE (ulpi_wrapper) +{ +public: + + // Singleton + static ulpi_wrapper& getInstance(void) + { + static ulpi_wrapper instance("ulpi_wrapper"); + return instance; + } + + // Ports + sc_in ulpi_clk60_i; + sc_in ulpi_rst_i; + sc_in > ulpi_data_i; + sc_out > ulpi_data_o; + sc_in ulpi_dir_i; + sc_in ulpi_nxt_i; + sc_out ulpi_stp_o; + sc_in > reg_addr_i; + sc_in reg_stb_i; + sc_in reg_we_i; + sc_in > reg_data_i; + sc_out > reg_data_o; + sc_out reg_ack_o; + sc_in utmi_txvalid_i; + sc_out utmi_txready_o; + sc_out utmi_rxvalid_o; + sc_out utmi_rxactive_o; + sc_out utmi_rxerror_o; + sc_out > utmi_data_o; + sc_in > utmi_data_i; + sc_in > utmi_xcvrselect_i; + sc_in utmi_termselect_i; + sc_in > utmi_opmode_i; + sc_in utmi_dppulldown_i; + sc_in utmi_dmpulldown_i; + sc_out > utmi_linestate_o; + + // Simulation control + void stopSimulation() { m_stop.write(true); } + bool isStopped() { return m_stop.read(); } + + // Callbacks + int valueChangeCb(void); + int attachCb(void); + + // Members + sc_signal m_stop; + sc_signal m_clk; + sc_signal m_rst; + +private: + SC_CTOR(ulpi_wrapper) { m_stop.write(false); } +}; + +#endif \ No newline at end of file diff --git a/ulpi_wrapper/testbench/ulpi_wrapper_io.h b/ulpi_wrapper/testbench/ulpi_wrapper_io.h new file mode 100644 index 0000000..a0d3d76 --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_wrapper_io.h @@ -0,0 +1,103 @@ +#ifndef ULPI_WRAPPER_IO_H +#define ULPI_WRAPPER_IO_H + +#include +#include "ulpi_wrapper.h" + +SC_MODULE (ulpi_wrapper_io) +{ +public: + sc_in ulpi_clk60_i; + sc_in ulpi_rst_i; + sc_signal > ulpi_data_i; + sc_signal > ulpi_data_o; + sc_signal ulpi_dir_i; + sc_signal ulpi_nxt_i; + sc_signal ulpi_stp_o; + sc_signal > reg_addr_i; + sc_signal reg_stb_i; + sc_signal reg_we_i; + sc_signal > reg_data_i; + sc_signal > reg_data_o; + sc_signal reg_ack_o; + sc_signal utmi_txvalid_i; + sc_signal utmi_txready_o; + sc_signal utmi_rxvalid_o; + sc_signal utmi_rxactive_o; + sc_signal utmi_rxerror_o; + sc_signal > utmi_data_o; + sc_signal > utmi_data_i; + sc_signal > utmi_xcvrselect_i; + sc_signal utmi_termselect_i; + sc_signal > utmi_opmode_i; + sc_signal utmi_dppulldown_i; + sc_signal utmi_dmpulldown_i; + sc_signal > utmi_linestate_o; + + // Constructor + ulpi_wrapper_io(sc_module_name name, ulpi_wrapper *dut): + sc_module(name) + , ulpi_clk60_i ("ulpi_clk60_i") + , ulpi_rst_i ("ulpi_rst_i") + , ulpi_data_i ("ulpi_data_i") + , ulpi_data_o ("ulpi_data_o") + , ulpi_dir_i ("ulpi_dir_i") + , ulpi_nxt_i ("ulpi_nxt_i") + , ulpi_stp_o ("ulpi_stp_o") + , reg_addr_i ("reg_addr_i") + , reg_stb_i ("reg_stb_i") + , reg_we_i ("reg_we_i") + , reg_data_i ("reg_data_i") + , reg_data_o ("reg_data_o") + , reg_ack_o ("reg_ack_o") + , utmi_txvalid_i ("utmi_txvalid_i") + , utmi_txready_o ("utmi_txready_o") + , utmi_rxvalid_o ("utmi_rxvalid_o") + , utmi_rxactive_o ("utmi_rxactive_o") + , utmi_rxerror_o ("utmi_rxerror_o") + , utmi_data_o ("utmi_data_o") + , utmi_data_i ("utmi_data_i") + , utmi_xcvrselect_i ("utmi_xcvrselect_i") + , utmi_termselect_i ("utmi_termselect_i") + , utmi_opmode_i ("utmi_opmode_i") + , utmi_dppulldown_i ("utmi_dppulldown_i") + , utmi_dmpulldown_i ("utmi_dmpulldown_i") + , utmi_linestate_o ("utmi_linestate_o") + { + m_dut = dut; + + this->ulpi_clk60_i(m_dut->m_clk); + this->ulpi_rst_i(m_dut->m_rst); + + m_dut->ulpi_clk60_i(m_dut->m_clk); + m_dut->ulpi_rst_i(m_dut->m_rst); + m_dut->ulpi_data_i(ulpi_data_i); + m_dut->ulpi_data_o(ulpi_data_o); + m_dut->ulpi_dir_i(ulpi_dir_i); + m_dut->ulpi_nxt_i(ulpi_nxt_i); + m_dut->ulpi_stp_o(ulpi_stp_o); + m_dut->reg_addr_i(reg_addr_i); + m_dut->reg_stb_i(reg_stb_i); + m_dut->reg_we_i(reg_we_i); + m_dut->reg_data_i(reg_data_i); + m_dut->reg_data_o(reg_data_o); + m_dut->reg_ack_o(reg_ack_o); + m_dut->utmi_txvalid_i(utmi_txvalid_i); + m_dut->utmi_txready_o(utmi_txready_o); + m_dut->utmi_rxvalid_o(utmi_rxvalid_o); + m_dut->utmi_rxactive_o(utmi_rxactive_o); + m_dut->utmi_rxerror_o(utmi_rxerror_o); + m_dut->utmi_data_o(utmi_data_o); + m_dut->utmi_data_i(utmi_data_i); + m_dut->utmi_xcvrselect_i(utmi_xcvrselect_i); + m_dut->utmi_termselect_i(utmi_termselect_i); + m_dut->utmi_opmode_i(utmi_opmode_i); + m_dut->utmi_dppulldown_i(utmi_dppulldown_i); + m_dut->utmi_dmpulldown_i(utmi_dmpulldown_i); + m_dut->utmi_linestate_o(utmi_linestate_o); + } + + ulpi_wrapper *m_dut; +}; + +#endif \ No newline at end of file diff --git a/ulpi_wrapper/testbench/ulpi_wrapper_tb.cpp b/ulpi_wrapper/testbench/ulpi_wrapper_tb.cpp new file mode 100644 index 0000000..74e0028 --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_wrapper_tb.cpp @@ -0,0 +1,198 @@ +#include "ulpi_wrapper_tb.h" + +//----------------------------------------------------------------- +// DUT interface +//----------------------------------------------------------------- +ulpi_wrapper *u_dut = NULL; +ulpi_wrapper_tb *u_tb = NULL; +sc_clock *u_clk = NULL; + +//----------------------------------------------------------------- +// sc_main_tb +//----------------------------------------------------------------- +void sc_main_tb(void) +{ + u_dut = &ulpi_wrapper::getInstance(); + u_tb = new ulpi_wrapper_tb("ulpi_wrapper_tb", u_dut); + + // Initialize SystemC + sc_start(0, SC_NS); +} +//----------------------------------------------------------------- +// testbench +//----------------------------------------------------------------- +void ulpi_wrapper_tb::testbench(void) +{ + sc_uint <8> last_wr = 0xFF; + + while (ulpi_rst_i.read()) + wait(); + + m_reg.write(ULPI_REG_SCRATCH, last_wr); + + int cycles = 0; + while (true) + { + // Random delay + int wait_len = rand() % 10; + if (wait_len) + wait(wait_len); + + // Random register write + if (rand() & 1) + { + last_wr = rand(); + m_reg.write(ULPI_REG_SCRATCH, last_wr); + } + // Random register read + else + { + sc_assert(m_reg.read(ULPI_REG_SCRATCH) == last_wr); + } + + if (!(rand() % 32)) + { + if (rand() & 1) + utmi_opmode_i.write(rand()); + else + utmi_dppulldown_i.write(rand() & 1); + } + + if (cycles++ == 1000) + m_dut->stopSimulation(); + } +} +//----------------------------------------------------------------- +// phy_rx: PHY Rx Thread +//----------------------------------------------------------------- +void ulpi_wrapper_tb::phy_rx(void) +{ + sc_uint <8> data; + sc_uint <1> last; + + sc_uint <8> ulpi_data; + sc_uint <1> ulpi_last; + + while (ulpi_rst_i.read()) + wait(); + + while (1) + { + // Wait for data from ULPI interface + ulpi_last = m_ulpi.read(ulpi_data); + + // Read actual data FIFO + (last, data) = m_link_phy_queue.read(); + + cout << hex << "EXPECT: DATA " << data << " LAST " << last << endl; + cout << hex << "GOT: DATA " << ulpi_data << " LAST " << ulpi_last << endl; + + sc_assert(ulpi_data == data); + sc_assert(ulpi_last == last); + } +} +//----------------------------------------------------------------- +// link_rx: Link Rx Thread +//----------------------------------------------------------------- +void ulpi_wrapper_tb::link_rx(void) +{ + sc_uint <8> data; + sc_uint <1> last; + + sc_uint <8> ulpi_data; + sc_uint <1> ulpi_last; + + while (ulpi_rst_i.read()) + wait(); + + while (1) + { + // Wait for data from UTMI interface + ulpi_last = m_utmi.read(ulpi_data); + + // Read actual data FIFO + (last, data) = m_phy_link_queue.read(); + + cout << hex << "EXPECT: DATA " << data << " LAST " << last << endl; + cout << hex << "GOT: DATA " << ulpi_data << " LAST " << ulpi_last << endl; + + sc_assert(ulpi_data == data); + sc_assert(ulpi_last == last); + } +} +//----------------------------------------------------------------- +// phy_tx: PHY Tx Thread +//----------------------------------------------------------------- +void ulpi_wrapper_tb::phy_tx(void) +{ + while (ulpi_rst_i.read()) + wait(); + + while (1) + { + wait(10 + (rand() % 16)); + + m_mutex.lock(); + + int len = 1 + (rand() % 8); + while (len--) + { + sc_uint <8> data = rand(); + sc_uint <1> last = (len == 0); + + cout << hex << "QUEUE (RX): DATA " << data << " LAST " << last << endl; + m_phy_link_queue.write((last, data)); + m_ulpi.write(data, last); + } + + do + { + wait(1); + } + while (m_phy_link_queue.num_available()); + + m_mutex.unlock(); + } +} +//----------------------------------------------------------------- +// link_tx: Link Tx Thread +//----------------------------------------------------------------- +void ulpi_wrapper_tb::link_tx(void) +{ + while (ulpi_rst_i.read()) + wait(); + + while (1) + { + wait(10 + (rand() % 16)); + + m_mutex.lock(); + + int len = 1 + (rand() % 8); + bool first = true; + while (len--) + { + sc_uint <8> data = rand(); + sc_uint <1> last = (len == 0); + + // First byte is PID + if (first) + data.range(7,4) = ~data.range(3,0); + + first = false; + + cout << hex << "QUEUE (TX): DATA " << data << " LAST " << last << endl; + m_link_phy_queue.write((last, data)); + m_utmi.write(data, last); + } + + // Wait until transfer completed + do + { + wait(); + } + while (m_link_phy_queue.num_available()); + + m_mutex.unlock(); + } +} diff --git a/ulpi_wrapper/testbench/ulpi_wrapper_tb.h b/ulpi_wrapper/testbench/ulpi_wrapper_tb.h new file mode 100644 index 0000000..337f404 --- /dev/null +++ b/ulpi_wrapper/testbench/ulpi_wrapper_tb.h @@ -0,0 +1,66 @@ +#ifndef ULPI_WRAPPER_TB_H +#define ULPI_WRAPPER_TB_H + +#include "ulpi_wrapper_io.h" +#include "ulpi_driver.h" +#include "utmi_driver.h" +#include "wbl_driver.h" + +class ulpi_wrapper_tb: public ulpi_wrapper_io +{ +public: + SC_HAS_PROCESS(ulpi_wrapper_tb); + ulpi_wrapper_tb(sc_module_name name, ulpi_wrapper *dut): ulpi_wrapper_io(name, dut), + m_ulpi("m_ulpi"), m_utmi("m_utmi"), m_reg("m_reg"), + m_phy_link_queue(2048), m_link_phy_queue(2048) + { + m_ulpi.clk_i(ulpi_clk60_i); + m_ulpi.rst_i(ulpi_rst_i); + + m_ulpi.ulpi_data_o(ulpi_data_i); + m_ulpi.ulpi_data_i(ulpi_data_o); + m_ulpi.ulpi_dir_o(ulpi_dir_i); + m_ulpi.ulpi_nxt_o(ulpi_nxt_i); + m_ulpi.ulpi_stp_i(ulpi_stp_o); + + m_utmi.clk_i(ulpi_clk60_i); + m_utmi.rst_i(ulpi_rst_i); + + m_utmi.utmi_txvalid_o(utmi_txvalid_i); + m_utmi.utmi_data_o(utmi_data_i); + m_utmi.utmi_txready_i(utmi_txready_o); + + m_utmi.utmi_data_i(utmi_data_o); + m_utmi.utmi_rxvalid_i(utmi_rxvalid_o); + m_utmi.utmi_rxactive_i(utmi_rxactive_o); + + m_reg.addr_o(reg_addr_i); + m_reg.data_o(reg_data_i); + m_reg.data_i(reg_data_o); + m_reg.we_o(reg_we_i); + m_reg.stb_o(reg_stb_i); + m_reg.ack_i(reg_ack_o); + + SC_CTHREAD(testbench, ulpi_clk60_i); + SC_CTHREAD(phy_tx, ulpi_clk60_i); + SC_CTHREAD(phy_rx, ulpi_clk60_i); + SC_CTHREAD(link_rx, ulpi_clk60_i); + SC_CTHREAD(link_tx, ulpi_clk60_i); + } + + ulpi_driver m_ulpi; + utmi_driver m_utmi; + wbl_driver m_reg; + + sc_fifo < sc_uint <9> > m_phy_link_queue; + sc_fifo < sc_uint <9> > m_link_phy_queue; + sc_mutex m_mutex; + + void testbench(void); + void phy_tx(void); + void phy_rx(void); + void link_rx(void); + void link_tx(void); +}; + +#endif diff --git a/ulpi_wrapper/testbench/utmi_driver.cpp b/ulpi_wrapper/testbench/utmi_driver.cpp new file mode 100644 index 0000000..f6eb834 --- /dev/null +++ b/ulpi_wrapper/testbench/utmi_driver.cpp @@ -0,0 +1,122 @@ +#include "utmi_driver.h" + +//----------------------------------------------------------------- +// tx_drive +//----------------------------------------------------------------- +void utmi_driver::tx_drive(void) +{ + // Wait until reset complete + while (rst_i.read()) + wait(); + + // I/O + // utmi_txvalid_o + // utmi_data_o + // utmi_txready_i + + utmi_txvalid_o.write(false); + utmi_data_o.write(false); + + while (true) + { + bool last; + sc_uint <8> data; + + do + { + last = tx_read(data); + + utmi_txvalid_o.write(true); + utmi_data_o.write(data); + + do + { + wait(); + } + while (!utmi_txready_i.read()); + + utmi_txvalid_o.write(false); + utmi_data_o.write(0); + } + while (!last); + + wait(); + } +} +//----------------------------------------------------------------- +// rx_mon +//----------------------------------------------------------------- +void utmi_driver::rx_mon(void) +{ + // Wait until reset complete + while (rst_i.read()) + wait(); + + // I/O + // utmi_data_i + // utmi_rxvalid_i + // utmi_rxactive_i + + bool last_valid = false; + sc_uint <8> last_data = 0; + while (true) + { + if (utmi_rxvalid_i.read()) + { + if (last_valid) + rx_write(last_data, false); + + last_valid = true; + last_data = utmi_data_i.read(); + } + + if (!utmi_rxactive_i.read() && last_valid) + { + rx_write(last_data, true); + last_valid = false; + } + wait(); + } +} +//----------------------------------------------------------------- +// write +//----------------------------------------------------------------- +void utmi_driver::write(sc_uint <8> data, bool last) +{ + sc_uint <9> fifo_data; + + fifo_data.range(7,0) = data; + fifo_data.range(8,8) = last; + + m_tx_fifo.write(fifo_data); +} +//----------------------------------------------------------------- +// read +//----------------------------------------------------------------- +bool utmi_driver::read(sc_uint <8> &data) +{ + sc_uint <9> fifo_data = m_rx_fifo.read(); + data = fifo_data.range(7,0); + return (bool)fifo_data.range(8,8); +} +//----------------------------------------------------------------- +// rx_write +//----------------------------------------------------------------- +void utmi_driver::rx_write(sc_uint <8> data, bool last) +{ + sc_uint <9> fifo_data; + + fifo_data.range(7,0) = data; + fifo_data.range(8,8) = last; + + m_rx_fifo.write(fifo_data); +} +//----------------------------------------------------------------- +// tx_read +//----------------------------------------------------------------- +bool utmi_driver::tx_read(sc_uint <8> &data) +{ + sc_uint <9> fifo_data = m_tx_fifo.read(); + data = fifo_data.range(7,0); + return (bool)fifo_data.range(8,8); +} diff --git a/ulpi_wrapper/testbench/utmi_driver.h b/ulpi_wrapper/testbench/utmi_driver.h new file mode 100644 index 0000000..83c9d8d --- /dev/null +++ b/ulpi_wrapper/testbench/utmi_driver.h @@ -0,0 +1,77 @@ +#ifndef UTMI_DRIVER_H +#define UTMI_DRIVER_H + +#include + +//------------------------------------------------------------- +// utmi_driver: UTMI driver (LINK) component +//------------------------------------------------------------- +SC_MODULE (utmi_driver) +{ +public: + //------------------------------------------------------------- + // Interface I/O + //------------------------------------------------------------- + // Clock and Reset + sc_in clk_i; + sc_in rst_i; + + // I/O + sc_out utmi_txvalid_o; + sc_out > utmi_data_o; + sc_in utmi_txready_i; + + sc_in > utmi_data_i; + sc_in utmi_rxvalid_i; + sc_in utmi_rxactive_i; + + //------------------------------------------------------------- + // Constructor + //------------------------------------------------------------- + SC_HAS_PROCESS(utmi_driver); + utmi_driver(sc_module_name name): sc_module(name), + m_tx_fifo(2048), + m_rx_fifo(2048) + { + SC_CTHREAD(tx_drive, clk_i.pos()); + SC_CTHREAD(rx_mon, clk_i.pos()); + } + + //------------------------------------------------------------- + // Trace + //------------------------------------------------------------- + void add_trace(sc_trace_file *vcd, std::string prefix) + { + #undef TRACE_SIGNAL + #define TRACE_SIGNAL(s) sc_trace(vcd,s,prefix + #s) + + TRACE_SIGNAL(utmi_txvalid_o); + TRACE_SIGNAL(utmi_data_o); + TRACE_SIGNAL(utmi_txready_i); + TRACE_SIGNAL(utmi_data_i); + TRACE_SIGNAL(utmi_rxvalid_i); + TRACE_SIGNAL(utmi_rxactive_i); + + #undef TRACE_SIGNAL + } + + //------------------------------------------------------------- + // API + //------------------------------------------------------------- + void write(sc_uint <8> data, bool last); + bool read(sc_uint <8> &data); + + //------------------------------------------------------------- + // Internal + //------------------------------------------------------------- +protected: + void tx_drive(void); + void rx_mon(void); + void rx_write(sc_uint <8> data, bool last); + bool tx_read(sc_uint <8> &data); + + sc_fifo < sc_uint<9> > m_tx_fifo; + sc_fifo < sc_uint<9> > m_rx_fifo; +}; + +#endif \ No newline at end of file diff --git a/ulpi_wrapper/testbench/wbl_driver.cpp b/ulpi_wrapper/testbench/wbl_driver.cpp new file mode 100644 index 0000000..c68014e --- /dev/null +++ b/ulpi_wrapper/testbench/wbl_driver.cpp @@ -0,0 +1,46 @@ +#include "wbl_driver.h" + +//----------------------------------------------------------------- +// write +//----------------------------------------------------------------- +void wbl_driver::write(sc_uint <8> addr, sc_uint <8> data) +{ + addr_o.write(addr); + data_o.write(data); + we_o.write(true); + stb_o.write(true); + + do + { + wait(); + + addr_o.write(0); + data_o.write(0); + we_o.write(false); + stb_o.write(false); + } + while (!ack_i.read()); +} +//----------------------------------------------------------------- +// read +//----------------------------------------------------------------- +sc_uint <8> wbl_driver::read(sc_uint <8> addr) +{ + addr_o.write(addr); + data_o.write(0); + we_o.write(false); + stb_o.write(true); + + do + { + wait(); + + addr_o.write(0); + data_o.write(0); + we_o.write(false); + stb_o.write(false); + } + while (!ack_i.read()); + + return data_i.read(); +} diff --git a/ulpi_wrapper/testbench/wbl_driver.h b/ulpi_wrapper/testbench/wbl_driver.h new file mode 100644 index 0000000..d39dbc3 --- /dev/null +++ b/ulpi_wrapper/testbench/wbl_driver.h @@ -0,0 +1,53 @@ +#ifndef WBL_DRIVER_H +#define WBL_DRIVER_H + +#include + +//------------------------------------------------------------- +// wbl_driver: Wishbone driver interface (8-bit A, 8-bit D) +//------------------------------------------------------------- +SC_MODULE(wbl_driver) +{ +public: + //------------------------------------------------------------- + // Interface I/O + //------------------------------------------------------------- + sc_out > addr_o; + sc_out > data_o; + sc_in > data_i; + sc_out we_o; + sc_out stb_o; + sc_in ack_i; + + //------------------------------------------------------------- + // Constructor + //------------------------------------------------------------- + SC_HAS_PROCESS(wbl_driver); + wbl_driver(sc_module_name name): sc_module(name) { } + + //------------------------------------------------------------- + // Trace + //------------------------------------------------------------- + void add_trace(sc_trace_file *vcd, std::string prefix) + { + #undef TRACE_SIGNAL + #define TRACE_SIGNAL(s) sc_trace(vcd,s,prefix + #s) + + TRACE_SIGNAL(addr_o); + TRACE_SIGNAL(data_o); + TRACE_SIGNAL(data_i); + TRACE_SIGNAL(we_o); + TRACE_SIGNAL(stb_o); + TRACE_SIGNAL(ack_i); + + #undef TRACE_SIGNAL + } + + //------------------------------------------------------------- + // API + //------------------------------------------------------------- + void write(sc_uint <8> addr, sc_uint <8> data); + sc_uint <8> read(sc_uint <8> addr); +}; + +#endif \ No newline at end of file