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

Subversion Repositories spdif_transmitter

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /spdif_transmitter
    from Rev 2 to Rev 3
    Reverse comparison

Rev 2 → Rev 3

/trunk/testbench/spdif_decoder.h
0,0 → 1,46
#ifndef SPDIF_DECODER_H
#define SPDIF_DECODER_H
 
#include <systemc.h>
 
#define SPDIF_MAX_BITS 24
 
//-------------------------------------------------------------
// spdif_decoder: Decoder for SPDIF signal
//-------------------------------------------------------------
SC_MODULE (spdif_decoder)
{
public:
// Clock and Reset
sc_in <bool> clk_i;
sc_in <bool> rst_i;
 
// I/O
sc_in <bool> rx_i;
 
// Constructor
SC_HAS_PROCESS(spdif_decoder);
spdif_decoder(sc_module_name name): sc_module(name),
m_rx_fifo(1024)
{
m_divisor = 0;
m_bits = 16;
SC_CTHREAD(input, clk_i.pos());
}
 
public:
sc_uint <SPDIF_MAX_BITS> read(void) { return m_rx_fifo.read(); }
bool read_ready(void) { return m_rx_fifo.num_available() > 0; }
void set_clock_div(int divisor) { m_divisor = divisor; }
void set_bit_width(int bits) { m_bits = bits; }
 
private:
void input(void);
 
sc_fifo < sc_uint<SPDIF_MAX_BITS> > m_rx_fifo;
 
int m_divisor;
int m_bits;
};
 
#endif
/trunk/testbench/spdif_tb.h
0,0 → 1,65
#ifndef SPDIF_TB_H
#define SPDIF_TB_H
 
#include "sc_vpi_clock.h"
#include "spdif_vpi.h"
 
#include "spdif_decoder.h"
#include "spdif_driver.h"
 
#define AUDIO_CLK_DIV 4
 
class spdif_tb: public sc_module
{
public:
SC_HAS_PROCESS(spdif_tb);
 
sc_signal <bool> rst_i;
sc_signal <bool> audio_clk_i;
sc_signal <bool> spdif_o;
sc_signal <sc_uint<32> > sample_i;
sc_signal <bool> sample_req_o;
 
spdif_tb(sc_module_name name): sc_module(name),
m_dut("tb_top"),
m_vpi_clk("tb_top.clk_i"),
m_decoder("m_decoder"),
m_driver("m_driver"),
m_tx_fifo("m_tx_fifo")
{
m_dut.clk_i(m_vpi_clk.m_clk);
m_dut.rst_i(rst_i);
m_dut.audio_clk_i(audio_clk_i);
m_dut.spdif_o(spdif_o);
m_dut.sample_i(sample_i);
m_dut.sample_req_o(sample_req_o);
 
m_decoder.clk_i(m_vpi_clk.m_clk);
m_decoder.rst_i(rst_i);
m_decoder.rx_i(spdif_o);
m_decoder.set_clock_div(AUDIO_CLK_DIV);
 
m_driver.clk_i(m_vpi_clk.m_clk);
m_driver.rst_i(rst_i);
m_driver.sample_req_i(sample_req_o);
m_driver.sample_data_o(sample_i);
 
SC_CTHREAD(drive, m_vpi_clk.m_clk);
SC_CTHREAD(monitor, m_vpi_clk.m_clk);
 
SC_CTHREAD(audio_clk, m_vpi_clk.m_clk);
}
 
spdif_vpi m_dut;
sc_vpi_clock m_vpi_clk;
spdif_decoder m_decoder;
spdif_driver m_driver;
 
sc_fifo < sc_uint<32> > m_tx_fifo;
 
void drive(void);
void monitor(void);
void audio_clk(void);
};
 
#endif
/trunk/testbench/spdif_driver.cpp
0,0 → 1,20
#include "spdif_driver.h"
 
//-----------------------------------------------------------------
// output: Drive tx data
//-----------------------------------------------------------------
void spdif_driver::output(void)
{
wait();
sc_assert(m_tx_fifo.num_available() > 0);
 
while (true)
{
sample_data_o.write(m_tx_fifo.read());
 
wait();
 
while (!sample_req_i.read())
wait();
}
}
/trunk/testbench/sc_vpi_clock.h
0,0 → 1,72
#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;
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_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);
 
// 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/spdif_driver.h
0,0 → 1,38
#ifndef SPDIF_DRIVER_H
#define SPDIF_DRIVER_H
 
#include <systemc.h>
 
//-------------------------------------------------------------
// spdif_driver:
//-------------------------------------------------------------
SC_MODULE (spdif_driver)
{
public:
// Clock and Reset
sc_in <bool> clk_i;
sc_in <bool> rst_i;
 
// I/O
sc_in <bool> sample_req_i;
sc_out <sc_uint<32> > sample_data_o;
 
// Constructor
SC_HAS_PROCESS(spdif_driver);
spdif_driver(sc_module_name name): sc_module(name),
m_tx_fifo(2048)
{
SC_CTHREAD(output, clk_i.pos());
}
 
public:
void write(sc_uint <32> data) { m_tx_fifo.write(data); }
bool write_empty(void) { return m_tx_fifo.num_available() == 0; }
 
private:
void output(void);
 
sc_fifo < sc_uint<32> > m_tx_fifo;
};
 
#endif
/trunk/testbench/spdif_vpi.h
0,0 → 1,48
#ifndef SPDIF_VPI_H
#define SPDIF_VPI_H
 
#include "sc_vpi_module.h"
 
class spdif_vpi: public sc_vpi_module
{
public:
sc_in <bool> clk_i;
sc_in <bool> rst_i;
sc_in <bool> audio_clk_i;
sc_out <bool> spdif_o;
sc_in <sc_uint<32> > sample_i;
sc_out <bool> sample_req_o;
 
void read_outputs(void)
{
sc_vpi_module_read_output_int(spdif_o, "spdif_o");
sc_vpi_module_read_output_int(sample_req_o, "sample_req_o");
}
void write_inputs(void)
{
sc_vpi_module_write_input_int(clk_i, "clk_i");
sc_vpi_module_write_input_int(rst_i, "rst_i");
sc_vpi_module_write_input_int(audio_clk_i, "audio_clk_i");
sc_vpi_module_write_input_int(sample_i, "sample_i");
}
 
spdif_vpi(sc_module_name name):
sc_vpi_module(name)
, clk_i ("clk_i")
, rst_i ("rst_i")
, audio_clk_i ("audio_clk_i")
, spdif_o ("spdif_o")
, sample_i ("sample_i")
, sample_req_o ("sample_req_o")
{
register_signal("clk_i");
register_signal("rst_i");
register_signal("audio_clk_i");
register_signal("spdif_o");
register_signal("sample_i");
register_signal("sample_req_o");
}
};
 
#endif
/trunk/testbench/spdif_decoder.cpp
0,0 → 1,116
#include "spdif_decoder.h"
 
#define PREAMBLE_Z 0x17
#define PREAMBLE_Y 0x27
#define PREAMBLE_X 0x47
 
#define SPDIF_PREAMBLE_L 0
#define SPDIF_PREAMBLE_H 3
#define SPDIF_PREAMBLE_W 4
 
#define SPDIF_SAMPLE_L 4
#define SPDIF_SAMPLE_H 27
#define SPDIF_SAMPLE_W 24
 
#define SPDIF_NVALID_L 28
#define SPDIF_NVALID_H 28
#define SPDIF_NVALID_W 1
 
#define SPDIF_PARITY_L 31
#define SPDIF_PARITY_H 31
#define SPDIF_PARITY_W 1
 
#define SPDIF_FIELD(v,n) v.range(SPDIF_##n##_H, SPDIF_##n##_L)
 
//-----------------------------------------------------------------
// input: Handle rx data
//-----------------------------------------------------------------
void spdif_decoder::input(void)
{
sc_uint <64> bmc_data = 0;
sc_uint <32> data = 0;
 
do
{
wait();
}
while (rst_i.read());
 
bool last_rx = false;
int subframe = 0;
bool rx = false;
while (true)
{
rx = rx_i.read();
 
// Detect transition in preamble
if (rx == false && last_rx == true)
{
// Wait for half a bit time to sample mid bit
wait(m_divisor/2);
 
// Preamble leading bits were 0111
bmc_data = 0x7;
 
// Capture remaining timeslots
for (int i=4;i<64;i++)
{
wait(m_divisor);
rx = rx_i.read();
bmc_data[i] = rx;
}
 
// Check preamble type is expected
if (subframe == 0)
sc_assert(bmc_data.range(7,0) == PREAMBLE_Z);
else if (subframe & 1)
sc_assert(bmc_data.range(7,0) == PREAMBLE_Y);
else
sc_assert(bmc_data.range(7,0) == PREAMBLE_X);
 
if (++subframe == 384)
subframe = 0;
 
// Decode BMC data
for (int i=0;i<64;i+=2)
{
if (bmc_data[i+0] != bmc_data[i+1])
data[i/2] = 1;
else
data[i/2] = 0;
}
 
// Check parity
int ones_count = 0;
for (int i=SPDIF_PREAMBLE_H+1;i<SPDIF_PARITY_L;i++)
if (data[i])
ones_count += 1;
 
if (ones_count & 1)
sc_assert(SPDIF_FIELD(data, PARITY));
else
sc_assert(!SPDIF_FIELD(data, PARITY));
 
sc_uint <SPDIF_SAMPLE_W> sample = SPDIF_FIELD(data, SAMPLE);
 
// Valid sample
if (!SPDIF_FIELD(data, NVALID))
{
sc_assert(m_rx_fifo.num_free() > 0);
 
if (m_bits == 16)
m_rx_fifo.write(sample >> (SPDIF_SAMPLE_W - 16));
else if (m_bits == 20)
m_rx_fifo.write(sample >> (SPDIF_SAMPLE_W - 20));
else if (m_bits == 24)
m_rx_fifo.write(sample >> (SPDIF_SAMPLE_W - 24));
else
sc_assert(!"Unsupported bit width");
}
}
else
wait();
 
last_rx = rx;
}
}
/trunk/testbench/spdif_tb.cpp
0,0 → 1,96
#include "spdif_tb.h"
 
//-----------------------------------------------------------------
// sc_main_tb
//-----------------------------------------------------------------
static int attach_system_c(p_cb_data user_data)
{
spdif_tb * u_tb = new spdif_tb("spdif_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
};
 
//-----------------------------------------------------------------
// audio_clk: Produce audio clock
//-----------------------------------------------------------------
void spdif_tb::audio_clk(void)
{
// Reset
rst_i.write(true);
wait(5);
rst_i.write(false);
wait(1);
 
while (1)
{
wait(AUDIO_CLK_DIV/2);
audio_clk_i.write(!audio_clk_i.read());
}
}
//-----------------------------------------------------------------
// drive: Drive input sequence
//-----------------------------------------------------------------
void spdif_tb::drive(void)
{
sc_uint <32> data;
int interations = 2000;
 
// Drive input data
while (interations--)
{
data.range(15,0) = rand();
data.range(31,16) = rand();
m_tx_fifo.write(data.range(15,0));
m_tx_fifo.write(data.range(31,16));
 
m_driver.write(data);
}
}
//-----------------------------------------------------------------
// monitor: Check output data
//-----------------------------------------------------------------
void spdif_tb::monitor(void)
{
do
{
wait();
}
while (rst_i.read());
 
while (1)
{
sc_uint <16> data = m_tx_fifo.read();
sc_uint <16> rx_data = m_decoder.read();
 
printf("EXPECT: %04x\n", (unsigned)data);
printf("GOT: %04x\n", (unsigned)rx_data);
 
sc_assert(rx_data == data);
 
if (m_tx_fifo.num_available() == 0)
m_dut.stopSimulation();
}
}
/trunk/testbench/tb_top.v
0,0 → 1,40
`timescale 1ns / 1ns
 
//-----------------------------------------------------------------
// Module: Auto generated top
//-----------------------------------------------------------------
module tb_top();
 
reg clk_i;
reg rst_i;
reg audio_clk_i;
wire spdif_o;
reg [31:0] sample_i;
wire sample_req_o;
 
//-----------------------------------------------------------------
// DUT
//-----------------------------------------------------------------
spdif dut
(
.clk_i(clk_i)
, .rst_i(rst_i)
, .audio_clk_i(audio_clk_i)
, .spdif_o(spdif_o)
, .sample_i(sample_i)
, .sample_req_o(sample_req_o)
);
 
//-----------------------------------------------------------------
// Trace
//-----------------------------------------------------------------
initial
begin
if (`TRACE)
begin
$dumpfile("waveform.vcd");
$dumpvars(0,tb_top);
end
end
 
endmodule
/trunk/testbench/makefile
0,0 → 1,58
#########################################################
# Vars
#########################################################
SYSTEMC_HOME ?= /usr/local/systemc-2.3.1
 
TRACE ?= 1
 
DUT_NAME = spdif
RTL_DUT = ../rtl/spdif.v
 
#########################################################
# Source
#########################################################
SRC = $(wildcard *.cpp)
 
SRC_V = tb_top.v
SRC_V += $(RTL_DUT)
SRC_V += ../rtl/spdif_core.v
 
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/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/spdif_core.v
167,10 → 167,6
// Left Channel (but not start of block)?
else
preamble_r = PREAMBLE_X; // X(M)
 
// If previous timeslot ended with a 1, invert preamble
if (spdif_o)
preamble_r = ~preamble_r;
end
 
always @ (posedge rst_i or posedge clk_i )
/trunk/README.md
0,0 → 1,37
### SPDIF Transmitter
 
Github: [http://github.com/ultraembedded/cores](https://github.com/ultraembedded/cores/tree/master/spdif)
 
This is a simple SPDIF transmitter module written in Verilog.
 
This module can either generate its own audio clock by dividing down clk_i or can use an external audio clock to drive the output stream via audio_clk_i.
 
For external clocking mode, the audio_clk_i clock rate should be:
* 32KHz - 4.096MHz
* 44.1KHz - 5.6448MHz
* 48KHz - 6.144MHz
 
Note that in external clocking mode, the frequency of clk_i must be more than 4 x audio_clk_i frequency.
 
For internal clocking mode, the clk_i input is divided to roughly the right frequency required for chosen the sample rate. This isn't going to be exact!
 
The input interface expects 32-bits (2 x 16-bit audio samples) to be provided to it on 'sample_i' and held until 'sample_req_o' is pulsed (data pop request).
 
This allows connection to a simple FIFO for audio samples.
 
##### Testing
 
Tested on a Pioneer VSX D510 over TOSLINK and also on a cheap no-brand Ebay D/A converter.
 
The supplied testbench requires the SystemC libraries and Icarus Verilog, both of which are available for free.
 
##### Configuration
 
* CLK_RATE_KHZ - Clock speed (clk_i) in KHz
* AUDIO_RATE - Audio sample rate, e.g. 44100 or 48000
* AUDIO_CLK_SRC - Can be INTERNAL or EXTERNAL
 
##### Size / Performance
 
With the default configuration...
* the design contains 69 flops, 3 adders, 2 comparators, 11 multiplexers (according to ISE).

powered by: WebSVN 2.1.0

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