OpenCores
URL https://opencores.org/ocsvn/ulpi_wrapper/ulpi_wrapper/trunk

Subversion Repositories ulpi_wrapper

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /ulpi_wrapper
    from Rev 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_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.

powered by: WebSVN 2.1.0

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