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 2 to Rev 3
- ↔ Reverse comparison
Rev 2 → Rev 3
/trunk/testbench/simulation.svh
File deleted
\ No newline at end of file
/trunk/testbench/ulpi_utmi.v
File deleted
/trunk/testbench/gtksettings.sav
File deleted
/trunk/testbench/top_tb.sv
File deleted
/trunk/testbench/ulpi_wrapper_tb.cpp
0,0 → 1,215
#include "ulpi_wrapper_tb.h" |
|
//----------------------------------------------------------------- |
// sc_main_tb |
//----------------------------------------------------------------- |
static int attach_system_c(p_cb_data user_data) |
{ |
ulpi_wrapper_tb * u_tb = new ulpi_wrapper_tb("ulpi_wrapper_tb"); |
|
// Initialize SystemC |
sc_start(0, SC_NS); |
|
// Start clock |
u_tb->m_vpi_clk.start(); |
} |
//----------------------------------------------------------------- |
// _register |
//----------------------------------------------------------------- |
static void _register(void) |
{ |
s_cb_data cb_data_s; |
cb_data_s.user_data = NULL; |
cb_data_s.reason = cbStartOfSimulation; |
cb_data_s.cb_rtn = attach_system_c; |
cb_data_s.time = NULL; |
cb_data_s.value = NULL; |
vpi_register_cb(&cb_data_s); |
} |
|
void (*vlog_startup_routines[])() = |
{ |
_register, |
0 |
}; |
|
//----------------------------------------------------------------- |
// testbench |
//----------------------------------------------------------------- |
void ulpi_wrapper_tb::testbench(void) |
{ |
sc_uint <8> last_wr = 0xFF; |
|
ulpi_rst_i.write(true); |
wait(5); |
ulpi_rst_i.write(false); |
wait(1); |
|
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(); |
} |
} |
/trunk/testbench/utmi_driver.h
0,0 → 1,77
#ifndef UTMI_DRIVER_H |
#define UTMI_DRIVER_H |
|
#include <systemc.h> |
|
//------------------------------------------------------------- |
// utmi_driver: UTMI driver (LINK) component |
//------------------------------------------------------------- |
SC_MODULE (utmi_driver) |
{ |
public: |
//------------------------------------------------------------- |
// Interface I/O |
//------------------------------------------------------------- |
// Clock and Reset |
sc_in<bool> clk_i; |
sc_in<bool> rst_i; |
|
// I/O |
sc_out <bool> utmi_txvalid_o; |
sc_out <sc_uint<8> > utmi_data_o; |
sc_in <bool> utmi_txready_i; |
|
sc_in <sc_uint<8> > utmi_data_i; |
sc_in <bool> utmi_rxvalid_i; |
sc_in <bool> 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 |
/trunk/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(); |
} |
/trunk/testbench/sc_vpi_clock.h
0,0 → 1,88
#ifndef __SC_VPI_CLOCK_H__ |
#define __SC_VPI_CLOCK_H__ |
|
#include <systemc.h> |
#include <vpi_user.h> |
|
|
static int sc_vpi_clock_after_delay(p_cb_data cb_data); |
|
class sc_vpi_clock |
{ |
public: |
|
sc_signal <bool> m_clk; |
int m_low_ns; |
int m_high_ns; |
uint64_t m_last_time; |
|
sc_module_name m_name; |
|
vpiHandle m_vpi_handle; |
|
sc_vpi_clock(sc_module_name name) : m_clk(name), m_name(name) |
{ |
m_low_ns = 5; |
m_high_ns = 5; |
m_last_time = 0; |
|
m_vpi_handle = vpi_handle_by_name((const char*)name, NULL); |
sc_assert(m_vpi_handle != NULL); |
} |
|
void start(void) { after_delay(); } |
|
int after_delay(void) |
{ |
bool clk_next = !m_clk.read(); |
s_vpi_time vpi_time_s; |
s_cb_data cb_data_s; |
|
vpi_time_s.type = vpiSimTime; |
vpi_time_s.high = 0; |
vpi_time_s.low = 0; |
|
s_vpi_value value_s; |
value_s.format = vpiIntVal; |
value_s.value.integer = clk_next; |
vpi_put_value(m_vpi_handle, &value_s, &vpi_time_s, vpiInertialDelay); |
|
// Setup wait time |
vpi_time_s.high = 0; |
vpi_time_s.low = clk_next ? m_high_ns : m_low_ns; |
vpi_time_s.type = vpiSimTime; |
|
m_clk.write(clk_next); |
|
// 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-m_last_time),SC_NS); |
|
// Attach value change callbacks for outputs |
cb_data_s.user_data = (PLI_BYTE8*)this; |
cb_data_s.reason = cbAfterDelay; |
cb_data_s.cb_rtn = sc_vpi_clock_after_delay; |
cb_data_s.time = &vpi_time_s; |
cb_data_s.value = NULL; |
vpi_register_cb(&cb_data_s); |
} |
}; |
|
static int sc_vpi_clock_after_delay(p_cb_data cb_data) |
{ |
sc_vpi_clock *p = (sc_vpi_clock*)cb_data->user_data; |
return p->after_delay(); |
} |
|
|
#endif |
/trunk/testbench/ulpi_wrapper_tb.h
0,0 → 1,128
#ifndef ULPI_WRAPPER_TB_H |
#define ULPI_WRAPPER_TB_H |
|
#include "sc_vpi_clock.h" |
#include "ulpi_wrapper_vpi.h" |
|
#include "ulpi_driver.h" |
#include "utmi_driver.h" |
#include "wbl_driver.h" |
|
class ulpi_wrapper_tb: public sc_module |
{ |
public: |
SC_HAS_PROCESS(ulpi_wrapper_tb); |
|
sc_signal< bool > ulpi_rst_i; |
sc_signal< sc_uint<8> > ulpi_data_i; |
sc_signal< sc_uint<8> > ulpi_data_o; |
sc_signal< bool > ulpi_dir_i; |
sc_signal< bool > ulpi_nxt_i; |
sc_signal< bool > ulpi_stp_o; |
sc_signal< sc_uint<8> > reg_addr_i; |
sc_signal< bool > reg_stb_i; |
sc_signal< bool > reg_we_i; |
sc_signal< sc_uint<8> > reg_data_i; |
sc_signal< sc_uint<8> > reg_data_o; |
sc_signal< bool > reg_ack_o; |
sc_signal< bool > utmi_txvalid_i; |
sc_signal< bool > utmi_txready_o; |
sc_signal< bool > utmi_rxvalid_o; |
sc_signal< bool > utmi_rxactive_o; |
sc_signal< bool > utmi_rxerror_o; |
sc_signal< sc_uint<8> > utmi_data_o; |
sc_signal< sc_uint<8> > utmi_data_i; |
sc_signal< sc_uint<2> > utmi_xcvrselect_i; |
sc_signal< bool > utmi_termselect_i; |
sc_signal< sc_uint<2> > utmi_opmode_i; |
sc_signal< bool > utmi_dppulldown_i; |
sc_signal< bool > utmi_dmpulldown_i; |
sc_signal< sc_uint<2> > utmi_linestate_o; |
|
ulpi_wrapper_tb(sc_module_name name): sc_module(name), |
m_dut("tb_top"), |
m_vpi_clk("tb_top.ulpi_clk60_i"), |
m_ulpi("m_ulpi"), m_utmi("m_utmi"), m_reg("m_reg"), |
m_phy_link_queue(2048), m_link_phy_queue(2048) |
{ |
m_dut.ulpi_clk60_i(m_vpi_clk.m_clk); |
m_dut.ulpi_rst_i(ulpi_rst_i); |
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); |
|
m_ulpi.clk_i(m_vpi_clk.m_clk); |
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(m_vpi_clk.m_clk); |
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, m_vpi_clk.m_clk); |
SC_CTHREAD(phy_tx, m_vpi_clk.m_clk); |
SC_CTHREAD(phy_rx, m_vpi_clk.m_clk); |
SC_CTHREAD(link_rx, m_vpi_clk.m_clk); |
SC_CTHREAD(link_tx, m_vpi_clk.m_clk); |
} |
|
ulpi_wrapper_vpi m_dut; |
|
sc_vpi_clock m_vpi_clk; |
|
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 |
/trunk/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); |
} |
/trunk/testbench/wbl_driver.h
0,0 → 1,53
#ifndef WBL_DRIVER_H |
#define WBL_DRIVER_H |
|
#include <systemc.h> |
|
//------------------------------------------------------------- |
// wbl_driver: Wishbone driver interface (8-bit A, 8-bit D) |
//------------------------------------------------------------- |
SC_MODULE(wbl_driver) |
{ |
public: |
//------------------------------------------------------------- |
// Interface I/O |
//------------------------------------------------------------- |
sc_out <sc_uint<8> > addr_o; |
sc_out <sc_uint<8> > data_o; |
sc_in <sc_uint<8> > data_i; |
sc_out <bool> we_o; |
sc_out <bool> stb_o; |
sc_in <bool> 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 |
/trunk/testbench/ulpi_driver.h
0,0 → 1,117
#ifndef ULPI_DRIVER_H |
#define ULPI_DRIVER_H |
|
#include <systemc.h> |
|
//----------------------------------------------------------------- |
// 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<bool> clk_i; |
sc_in<bool> rst_i; |
|
// I/O |
sc_out <sc_uint<8> > ulpi_data_o; |
sc_in <sc_uint<8> > ulpi_data_i; |
sc_out <bool> ulpi_dir_o; |
sc_out <bool> ulpi_nxt_o; |
sc_in <bool> 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 |
/trunk/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); |
} |
/trunk/testbench/tb_top.v
0,0 → 1,80
`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) |
); |
|
//----------------------------------------------------------------- |
// Trace |
//----------------------------------------------------------------- |
initial |
begin |
if (`TRACE) |
begin |
$dumpfile("waveform.vcd"); |
$dumpvars(0,tb_top); |
end |
end |
|
endmodule |
/trunk/testbench/makefile
1,35 → 1,56
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 |
|
RTL_DUT = ../rtl/ulpi_wrapper.v |
|
######################################################### |
# Source |
######################################################### |
SRC = $(wildcard *.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 |
/trunk/testbench/ulpi_wrapper_vpi.h
0,0 → 1,128
#ifndef ULPI_WRAPPER_VPI_H |
#define ULPI_WRAPPER_VPI_H |
|
#include "sc_vpi_module.h" |
|
class ulpi_wrapper_vpi: public sc_vpi_module |
{ |
public: |
sc_in <bool> ulpi_clk60_i; |
sc_in <bool> ulpi_rst_i; |
sc_in <sc_uint<8> > ulpi_data_i; |
sc_out <sc_uint<8> > ulpi_data_o; |
sc_in <bool> ulpi_dir_i; |
sc_in <bool> ulpi_nxt_i; |
sc_out <bool> ulpi_stp_o; |
sc_in <sc_uint<8> > reg_addr_i; |
sc_in <bool> reg_stb_i; |
sc_in <bool> reg_we_i; |
sc_in <sc_uint<8> > reg_data_i; |
sc_out <sc_uint<8> > reg_data_o; |
sc_out <bool> reg_ack_o; |
sc_in <bool> utmi_txvalid_i; |
sc_out <bool> utmi_txready_o; |
sc_out <bool> utmi_rxvalid_o; |
sc_out <bool> utmi_rxactive_o; |
sc_out <bool> utmi_rxerror_o; |
sc_out <sc_uint<8> > utmi_data_o; |
sc_in <sc_uint<8> > utmi_data_i; |
sc_in <sc_uint<2> > utmi_xcvrselect_i; |
sc_in <bool> utmi_termselect_i; |
sc_in <sc_uint<2> > utmi_opmode_i; |
sc_in <bool> utmi_dppulldown_i; |
sc_in <bool> utmi_dmpulldown_i; |
sc_out <sc_uint<2> > utmi_linestate_o; |
|
void read_outputs(void) |
{ |
sc_vpi_module_read_output_int(ulpi_data_o, "ulpi_data_o"); |
sc_vpi_module_read_output_int(ulpi_stp_o, "ulpi_stp_o"); |
sc_vpi_module_read_output_int(reg_data_o, "reg_data_o"); |
sc_vpi_module_read_output_int(reg_ack_o, "reg_ack_o"); |
sc_vpi_module_read_output_int(utmi_txready_o, "utmi_txready_o"); |
sc_vpi_module_read_output_int(utmi_rxvalid_o, "utmi_rxvalid_o"); |
sc_vpi_module_read_output_int(utmi_rxactive_o, "utmi_rxactive_o"); |
sc_vpi_module_read_output_int(utmi_rxerror_o, "utmi_rxerror_o"); |
sc_vpi_module_read_output_int(utmi_data_o, "utmi_data_o"); |
sc_vpi_module_read_output_int(utmi_linestate_o, "utmi_linestate_o"); |
} |
|
void write_inputs(void) |
{ |
sc_vpi_module_write_input_int(ulpi_clk60_i, "ulpi_clk60_i"); |
sc_vpi_module_write_input_int(ulpi_rst_i, "ulpi_rst_i"); |
sc_vpi_module_write_input_int(ulpi_data_i, "ulpi_data_i"); |
sc_vpi_module_write_input_int(ulpi_dir_i, "ulpi_dir_i"); |
sc_vpi_module_write_input_int(ulpi_nxt_i, "ulpi_nxt_i"); |
sc_vpi_module_write_input_int(reg_addr_i, "reg_addr_i"); |
sc_vpi_module_write_input_int(reg_stb_i, "reg_stb_i"); |
sc_vpi_module_write_input_int(reg_we_i, "reg_we_i"); |
sc_vpi_module_write_input_int(reg_data_i, "reg_data_i"); |
sc_vpi_module_write_input_int(utmi_txvalid_i, "utmi_txvalid_i"); |
sc_vpi_module_write_input_int(utmi_data_i, "utmi_data_i"); |
sc_vpi_module_write_input_int(utmi_xcvrselect_i, "utmi_xcvrselect_i"); |
sc_vpi_module_write_input_int(utmi_termselect_i, "utmi_termselect_i"); |
sc_vpi_module_write_input_int(utmi_opmode_i, "utmi_opmode_i"); |
sc_vpi_module_write_input_int(utmi_dppulldown_i, "utmi_dppulldown_i"); |
sc_vpi_module_write_input_int(utmi_dmpulldown_i, "utmi_dmpulldown_i"); |
} |
|
ulpi_wrapper_vpi(sc_module_name name): |
sc_vpi_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") |
{ |
register_signal("ulpi_clk60_i"); |
register_signal("ulpi_rst_i"); |
register_signal("ulpi_data_i"); |
register_signal("ulpi_data_o"); |
register_signal("ulpi_dir_i"); |
register_signal("ulpi_nxt_i"); |
register_signal("ulpi_stp_o"); |
register_signal("reg_addr_i"); |
register_signal("reg_stb_i"); |
register_signal("reg_we_i"); |
register_signal("reg_data_i"); |
register_signal("reg_data_o"); |
register_signal("reg_ack_o"); |
register_signal("utmi_txvalid_i"); |
register_signal("utmi_txready_o"); |
register_signal("utmi_rxvalid_o"); |
register_signal("utmi_rxactive_o"); |
register_signal("utmi_rxerror_o"); |
register_signal("utmi_data_o"); |
register_signal("utmi_data_i"); |
register_signal("utmi_xcvrselect_i"); |
register_signal("utmi_termselect_i"); |
register_signal("utmi_opmode_i"); |
register_signal("utmi_dppulldown_i"); |
register_signal("utmi_dmpulldown_i"); |
register_signal("utmi_linestate_o"); |
} |
}; |
|
#endif |
/trunk/testbench/sc_vpi_module.h
0,0 → 1,145
#ifndef __SC_VPI_MODULE_H__ |
#define __SC_VPI_MODULE_H__ |
|
#include <systemc.h> |
#include <assert.h> |
#include <vpi_user.h> |
|
static int sc_vpi_module_value_change(p_cb_data cb_data); |
|
#define sc_vpi_module_read_output_int(obj, name) \ |
{ \ |
s_vpi_value value_s; \ |
s_vpi_time vpi_time_s; \ |
\ |
value_s.format = vpiIntVal; \ |
\ |
vpi_time_s.type = vpiSimTime; \ |
vpi_time_s.high = 0; \ |
vpi_time_s.low = 0; \ |
\ |
std::string path = m_hdl_name; \ |
path = path + "." + name; \ |
vpiHandle handle = vpi_handle_by_name(path.c_str(), NULL); \ |
assert(handle != NULL); \ |
\ |
vpi_get_value(handle, &value_s); \ |
obj.write(value_s.value.integer); \ |
} |
|
#define sc_vpi_module_write_input_int(obj, name) \ |
{ \ |
s_vpi_value value_s; \ |
s_vpi_time vpi_time_s; \ |
\ |
value_s.format = vpiIntVal; \ |
\ |
vpi_time_s.type = vpiSimTime; \ |
vpi_time_s.high = 0; \ |
vpi_time_s.low = 0; \ |
\ |
std::string path = m_hdl_name; \ |
path = path + "." + name; \ |
vpiHandle handle = vpi_handle_by_name(path.c_str(), NULL); \ |
assert(handle != NULL); \ |
\ |
value_s.value.integer = obj.read(); \ |
vpi_put_value(handle, &value_s, &vpi_time_s, vpiInertialDelay); \ |
} |
|
class sc_vpi_module |
{ |
public: |
std::string m_hdl_name; |
uint64_t m_last_time; |
sc_signal<bool> m_stop; |
|
sc_vpi_module(sc_module_name name) : m_hdl_name((std::string)name) |
{ |
m_last_time = 0; |
m_stop.write(false); |
} |
|
// Simulation control |
void stopSimulation() { m_stop.write(true); } |
bool isStopped() { return m_stop.read(); } |
|
virtual void read_outputs(void) { } |
virtual void write_inputs(void) { } |
|
bool register_signal(const char *name) |
{ |
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 |
std::string path = m_hdl_name; |
path = path + "." + name; |
vpiHandle handle = vpi_handle_by_name(path.c_str(), NULL); |
if (!handle) |
return false; |
|
// Attach value change callbacks for outputs |
cb_data_s.user_data = (PLI_BYTE8*)this; |
cb_data_s.reason = cbValueChange; |
cb_data_s.cb_rtn = sc_vpi_module_value_change; |
cb_data_s.time = &vpi_time; |
cb_data_s.value = &value_s; |
|
value_s.format = vpiIntVal; |
|
cb_data_s.obj = handle; |
vpi_register_cb(&cb_data_s); |
|
return true; |
} |
|
int value_change(void) |
{ |
s_vpi_time vpi_time_s; |
|
vpi_time_s.type = vpiSimTime; |
vpi_time_s.high = 0; |
vpi_time_s.low = 0; |
|
// Outputs |
read_outputs(); |
|
// 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-m_last_time),SC_NS); |
|
m_last_time = time_value; |
|
// Inputs |
write_inputs(); |
|
if (isStopped()) |
vpi_sim_control(vpiFinish, 0); |
|
return 0; |
} |
}; |
|
static int sc_vpi_module_value_change(p_cb_data cb_data) |
{ |
sc_vpi_module *p = (sc_vpi_module*)cb_data->user_data; |
return p->value_change(); |
} |
|
#endif |
/trunk/rtl/ulpi_wrapper.v
48,6 → 48,15
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,11 → 80,20
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 |
//----------------------------------------------------------------- |
reg mode_update_q; |
83,6 → 101,8
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
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
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,7 → 169,65
|
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 |
//----------------------------------------------------------------- |
localparam TX_DELAY_W = 3; |
232,14 → 310,6
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
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
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
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
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
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
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 |
/trunk/README.md
1,5 → 1,7
### ULPI Link Wrapper |
|
Github: [http://github.com/ultraembedded/cores](https://github.com/ultraembedded/cores/tree/master/ulpi_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. |
|
18,10 → 20,11
|
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). |
* 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. |