Commit 484a0bc3 authored by Dimitris Lampridis's avatar Dimitris Lampridis

sim: first working version of SPEC FMC-ADC testbench.

TODO:

1. Remove hard-coded FMC-ADC values from WRTD testbench classes
2. Make it work for more than one rules
3. Finalise support for alarms
4. Add support for accessing the logs
parent 0ad009fa
......@@ -4,7 +4,7 @@
// https://ohwr.org/projects/wrtd
//------------------------------------------------------------------------------
//
// unit name: WrtdAlarm, WrtdAlarmCollection
// unit name: WrtdAlarm
//
// description: A SystemVerilog Class for a WRTD "alarm" repeated capability
//
......@@ -29,30 +29,54 @@
class WrtdAlarm extends WrtdRepCap;
protected WrtdTstamp setup_time;
protected wrtd_event ev;
protected int repeat_count;
protected uint32_t period_ns;
function new ( int core, int index, string name = "" );
super.new ( core, index, name );
this.setup_time = new();
this.ev.ts = new();
this.ev.id = new();
clear();
endfunction // new
function void clear ( );
super.clear();
this.setup_time.zero();
this.ev.ts.zero();
this.ev.id.clear();
this.ev.seq = 0;
this.ev.flags = 0;
this.repeat_count = 0;
this.period_ns = 0;
endfunction // clear
function repcap_data data_pack ( );
function wrtd_data data_pack ( );
wrtd_data ret = new[`WRTD_ALRM_WORD_SIZE];
ret[0:2] = this.setup_time.data_pack();
ret[3:5] = this.ev.ts.data_pack();
ret[6:9] = this.ev.id.data_pack();
ret[10] = this.ev.seq;
ret[11] = this.ev.flags;
ret[12] = this.enabled;
ret[13] = this.repeat_count;
ret[14] = this.period_ns;
return ret;
endfunction // data_pack
function void data_unpack ( repcap_data data );
function void data_unpack ( wrtd_data data );
this.setup_time.data_unpack ( data[0:2] );
this.ev.ts.data_unpack ( data[3:5] );
this.ev.id.data_unpack ( data[6:9] );
this.ev.seq = data[10];
this.ev.flags = data[11];
this.enabled = data[12];
this.repeat_count = data[13];
this.period_ns = data[14];
endfunction // data_unpack
endclass //WrtdAlarm
class WrtdAlarmCollection extends WrtdRepCapCollection;
protected WrtdAlarm collection[];
function new ( int size, string name );
super.new ( size, name );
endfunction // new
endclass // WrtdAlarmCollection
`endif // `ifndef __WRTD_ALARM_INCLUDED
......@@ -32,12 +32,15 @@
`define WRTD_ID_LEN 16
`define WRTD_IO_MSG_WORD_SIZE 3
`define WRTD_IO_MSG_WORD_SIZE 2
`define WRTD_CFG_MSG_WORD_SIZE 5
`define WRTD_ROOT_WORD_SIZE 13
`define WRTD_RULE_WORD_SIZE 40
`define WRTD_ALRM_WORD_SIZE 15
`define WRTD_DEST_CPU_LOCAL 'hfe
`define WRTD_DEST_CH_NET 'hff
enum {
WRTD_ACTION_GET_CONFIG,
WRTD_ACTION_READW,
......@@ -49,6 +52,12 @@ enum {
WRTD_DIR_OUTPUT
} wrtd_dir;
typedef uint32_t wrtd_data[];
function int wrtd_gen_hash ( string s );
return ( s[0] ^ s[3] ^ s[4] ^ s[7] );
endfunction // wrtd_gen_hash
class WrtdTstamp;
protected string name;
protected uint32_t seconds;
......@@ -78,12 +87,95 @@ class WrtdTstamp;
this.frac = frac;
endfunction // set
function uint32_t get_sec ( );
return this.seconds;
endfunction // get_sec
function uint32_t get_ns ( );
return this.ns;
endfunction // get_ns
function uint32_t get_frac ( );
return this.frac;
endfunction // get_frac
function wrtd_data data_pack ( );
wrtd_data ret=new[3];
ret[0] = this.seconds;
ret[1] = this.ns;
ret[2] = this.frac;
return ret;
endfunction // data_pack
function void data_unpack ( wrtd_data data);
this.seconds = data[0];
this.ns = data[1];
this.frac = data[2];
endfunction // data_unpack
function void zero ( );
set ( 0, 0, 0);
set ( 0, 0, 0 );
endfunction // zero
endclass // WrtdTstamp
class WrtdId;
protected string id;
function new ();
clear();
endfunction // new
function void clear ();
id = "";
endfunction // clear
function void set ( string id );
if ( id.len() > `WRTD_ID_LEN )
$error ( "length of string longer than the available storage" );
else
begin
this.clear();
this.id = id;
end
endfunction // set
function string get ( );
return this.id;
endfunction // get
function wrtd_data data_pack ( );
int i;
wrtd_data d;
d = new[`WRTD_ID_LEN / 4];
for ( i = 0; i < `WRTD_ID_LEN / 4; i ++ )
d[i] = 0;
for ( i = 0; i < this.id.len(); i ++ )
d[i/4] |= this.id[i] << ( 8 * ( i % 4 ) );
return d;
endfunction // data_pack
function void data_unpack ( wrtd_data data );
if ( data.size() > `WRTD_ID_LEN / 4 )
$error ( "length of data longer than the available storage" );
else
this.id = { <<32 { { <<8 { data } } } };
endfunction // data_unpack
function int is_empty ( );
return ( this.id.len() == 0 );
endfunction // is_empty
endclass // WrtdId
typedef struct {
WrtdTstamp ts;
WrtdId id;
uint32_t seq;
byte unsigned flags;
} wrtd_event;
typedef struct {
uint32_t addr;
uint32_t nbr_alarms;
......@@ -94,6 +186,36 @@ typedef struct {
uint32_t devs_addr[4];
} wrtd_root;
typedef struct {
int core;
int index;
int ch_en;
} wrtd_dev;
typedef struct {
string name;
uint32_t dir;
uint32_t nbr_chs;
string channels[];
/* -----\/----- EXCLUDED -----\/-----
enum wrtd_status (*configure)(struct wrtd_dev *wrtd,
unsigned cpu, unsigned dev,
unsigned int mask);
-----/\----- EXCLUDED -----/\----- */
} wrtd_mt_device;
typedef struct {
uint32_t fw_id;
uint32_t nbr_devices;
wrtd_mt_device devices[`WRTD_MAX_DEVS];
} wrtd_mt_cpu_config;
typedef struct {
uint32_t mt_app_id;
uint32_t nbr_cpus;
uint32_t tx_cpu;
uint32_t rx_cpu;
wrtd_mt_cpu_config cpus[`WRTD_MAX_CPUS];
} wrtd_mt_config;
`endif // `ifndef __WRTD_DEFINE_INCLUDED
......@@ -4,7 +4,7 @@
// https://ohwr.org/projects/wrtd
//------------------------------------------------------------------------------
//
// unit name: WrtdDev
// unit name: WrtdDrv
//
// description: A SystemVerilog Class to provide an abstraction of a complete
// WRTD device.
......@@ -23,23 +23,26 @@
// and limitations under the License.
//------------------------------------------------------------------------------
`ifndef __WRTD_DEV_INCLUDED
`define __WRTD_DEV_INCLUDED
`ifndef __WRTD_DRV_INCLUDED
`define __WRTD_DRV_INCLUDED
`include "wrtd_definitions.svh"
`include "wrtd_rule.svh"
`include "wrtd_alarm.svh"
`include "mock_turtle_driver.svh"
class WrtdDev;
class WrtdDrv;
uint32_t ready;
protected string name;
protected MockTurtleDriver mt;
protected uint32_t nbr_cpus;
protected uint32_t nbr_devs;
protected uint32_t hmq_words[];
protected wrtd_root roots[];
protected WrtdRuleCollection rules;
protected WrtdAlarmCollection alarms;
protected MockTurtleDriver mt;
protected uint32_t nbr_cpus;
protected uint32_t free_rule_slots[];
protected uint32_t hmq_words[];
protected wrtd_mt_config cfg;
protected wrtd_root roots[];
protected wrtd_dev devices[$];
protected WrtdRepCapCollection rules;
protected WrtdRepCapCollection alarms;
function new ( CBusAccessor acc, uint64_t base,
vIMockTurtleIRQ irq, string name = "" );
......@@ -57,19 +60,50 @@ class WrtdDev;
endtask // mdisplay
task init ( );
int i, j, n;
int i, j;
uint32_t val, cpu_ready, init_done, nbr_rules, nbr_alarms;
uint32_t val, cpu_ready;
WrtdRule new_rule;
WrtdAlarm new_alarm;
wrtd_dev new_dev;
uint32_t data[];
this.ready = 0;
mt.init ( );
this.nbr_cpus = mt.rom.getCoreCount ( );
// TODO: replace with non-hardcoded values
this.cfg.mt_app_id = 'h115790d1;
this.cfg.nbr_cpus = 1;
this.cfg.tx_cpu = 0;
this.cfg.rx_cpu = 0;
this.cfg.cpus[0].fw_id = 'h35b0;
this.cfg.cpus[0].nbr_devices = 2;
this.cfg.cpus[0].devices[0].name = "FMC ADCin 5CH";
this.cfg.cpus[0].devices[0].dir = WRTD_DIR_INPUT;
this.cfg.cpus[0].devices[0].nbr_chs = 5;
this.cfg.cpus[0].devices[0].channels = new[5];
this.cfg.cpus[0].devices[0].channels[0] = "ADCI1";
this.cfg.cpus[0].devices[0].channels[1] = "ADCI2";
this.cfg.cpus[0].devices[0].channels[2] = "ADCI3";
this.cfg.cpus[0].devices[0].channels[3] = "ADCI4";
this.cfg.cpus[0].devices[0].channels[4] = "ADCI5";
this.cfg.cpus[0].devices[1].name = "FMC ADCout";
this.cfg.cpus[0].devices[1].dir = WRTD_DIR_OUTPUT;
this.cfg.cpus[0].devices[1].nbr_chs = 1;
this.cfg.cpus[0].devices[1].channels = new[1];
this.cfg.cpus[0].devices[1].channels[0] = "ADCO1";
nbr_rules = 0;
this.nbr_cpus = mt.rom.getCoreCount ( );
nbr_alarms = 0;
this.free_rule_slots = new[this.nbr_cpus];
this.hmq_words = new[this.nbr_cpus];
......@@ -78,19 +112,18 @@ class WrtdDev;
for ( i = 0; i < this.nbr_cpus; i++ )
begin
this.hmq_words[i] =
`TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD(mt.rom.getHmqDimensions(i, 0));
`TRTL_CONFIG_ROM_MQ_SIZE_PAYLOAD( mt.rom.getHmqDimensions( i, 0 ) );
mt.enable_hmqi_irq ( i, 0, 1 );
mt.enable_console_irq ( i, 1 );
mt.reset_core ( i, 0 );
end
init_done = 0;
fork
begin
for ( i = 0; i < this.nbr_cpus; i++ )
begin
// wait for ready notification from firmware
cpu_ready = 0;
while ( cpu_ready == 0 )
begin
......@@ -100,43 +133,58 @@ class WrtdDev;
cpu_ready = 1;
# 1us;
end
// retrieve address of root
msg_get_config ( i, data );
this.roots[i].addr = data[0];
// retrieve root
msg_readw ( i, this.roots[i].addr, `WRTD_ROOT_WORD_SIZE, data );
this.roots[i].nbr_rules = (data[1] & 'h00ff0000) >> 16;
this.roots[i].nbr_alarms = (data[1] & 'h0000ff00) >> 8;
this.roots[i].nbr_devs = (data[1] & 'h000000ff) >> 0;
this.roots[i].nbr_rules = ( data[1] & 'h00ff0000 ) >> 16;
this.roots[i].nbr_alarms = ( data[1] & 'h0000ff00 ) >> 8;
this.roots[i].nbr_devs = ( data[1] & 'h000000ff ) >> 0;
this.roots[i].devs_addr[0:3] = data[4:7];
this.roots[i].rules_addr = data[8];
this.roots[i].alarms_addr = data[9];
this.nbr_devs += this.roots[i].nbr_devs;
nbr_rules += this.roots[i].nbr_rules;
nbr_alarms += this.roots[i].nbr_alarms;
// init free rule slots
this.free_rule_slots[i] = this.roots[i].nbr_rules;
end
this.rules = new ( nbr_rules, this.name );
n = 0;
// initialise rules
this.rules = new ( this.name );
for ( i = 0; i < this.nbr_cpus; i++ )
for ( j = 0; j < this.roots[i].nbr_rules; j++ )
begin
this.rules.init_element ( n, i, j );
n++;
new_rule = new ( i, j, this.name );
this.rules.collection.push_back ( new_rule );
end
this.alarms = new ( nbr_alarms, this.name );
n = 0;
// initialise alarms
this.alarms = new ( this.name );
for ( i = 0; i < this.nbr_cpus; i++ )
for ( j = 0; j < this.roots[i].nbr_alarms; j++ )
begin
this.alarms.init_element ( n, i, j );
n++;
new_alarm = new ( i, j, this.name );
this.alarms.collection.push_back ( new_alarm );
end
// initialise devices
this.devices.delete();
for ( i = 0; i < this.nbr_cpus; i++ )
for ( j = 0; j < this.roots[i].nbr_devs; j++ )
begin
new_dev.core = i;
new_dev.index = j;
new_dev.ch_en = 0;
this.devices.push_back ( new_dev );
end
init_done = 1;
// TODO remove hard-coded ADC init
adc_init ();
this.ready = 1;
end
begin
while ( init_done == 0 )
while ( this.ready == 0 )
begin
mt.update ( );
# 1us;
......@@ -144,7 +192,6 @@ class WrtdDev;
end
join
mdisplay("WRTD ready!");
endtask // init
task msg_get_config ( int core, ref uint32_t data[] );
......@@ -153,7 +200,7 @@ class WrtdDev;
MQueueMsg msg;
if ( data.size() < `WRTD_CFG_MSG_WORD_SIZE )
data = new[`WRTD_IO_MSG_WORD_SIZE];
data = new[`WRTD_CFG_MSG_WORD_SIZE];
msg = new ( core, 0 );
msg.header.flags = `TRTL_HMQ_HEADER_FLAG_RPC | `TRTL_HMQ_HEADER_FLAG_SYNC;
......@@ -162,7 +209,12 @@ class WrtdDev;
mt.hmq_send_message ( msg );
while ( mt.hmq_pending_messages ( msg.core, 0 ) == 0 ) #1us;
mt.hmq_receive_message ( msg );
for ( i = 0; i < `WRTD_IO_MSG_WORD_SIZE; i++ )
if ( msg.header.len != `WRTD_CFG_MSG_WORD_SIZE )
begin
$error ( "get_config: unexpected message header length" );
mdisplay ( $sformatf ( "message received: %s", msg.tostring() ) );
end
for ( i = 0; i < `WRTD_CFG_MSG_WORD_SIZE; i++ )
data[i] = msg.data[i];
endtask // msg_get_config
......@@ -178,19 +230,24 @@ class WrtdDev;
offset = 0;
while ( offset <= count )
while ( offset < count )
begin
tlen = this.hmq_words[core] - `WRTD_IO_MSG_WORD_SIZE;
if ( tlen > count )
tlen = count;
if ( tlen > count - offset )
tlen = count - offset;
msg = new ( core, 0 );
msg.header.flags = `TRTL_HMQ_HEADER_FLAG_RPC | `TRTL_HMQ_HEADER_FLAG_SYNC;
msg.header.msg_id = WRTD_ACTION_READW;
msg.header.len = `WRTD_IO_MSG_WORD_SIZE;
msg.data = '{ addr, tlen };
msg.data = '{ addr + offset, tlen };
mt.hmq_send_message ( msg );
while ( mt.hmq_pending_messages ( msg.core, 0 ) == 0 ) #1us;
mt.hmq_receive_message ( msg );
if ( msg.header.len != tlen )
begin
$error ( "readw: unexpected message header length" );
mdisplay ( $sformatf ( "message received: %s", msg.tostring() ) );
end
for ( i = 0; i < tlen; i++ )
data[offset+i] = msg.data[i];
offset += tlen;
......@@ -206,16 +263,16 @@ class WrtdDev;
offset = 0;
while ( offset <= count )
while ( offset < count )
begin
tlen = this.hmq_words[core] - `WRTD_IO_MSG_WORD_SIZE;
if ( tlen > count )
tlen = count;
if ( tlen > count - offset )
tlen = count - offset;
msg = new ( core, 0 );
msg.header.flags = `TRTL_HMQ_HEADER_FLAG_RPC | `TRTL_HMQ_HEADER_FLAG_SYNC;
msg.header.msg_id = WRTD_ACTION_WRITEW;
msg.header.len = `WRTD_IO_MSG_WORD_SIZE;
msg.data = '{ addr, tlen };
msg.header.len = `WRTD_IO_MSG_WORD_SIZE + count;
msg.data = '{ addr + offset, tlen };
for ( i = 0; i < tlen; i++ )
msg.data[i+2] = data[offset+i];
mt.hmq_send_message ( msg );
......@@ -226,10 +283,19 @@ class WrtdDev;
endtask // msg_writew
task adc_init ( );
uint32_t addr;
uint32_t data[1];
addr = this.roots[0].devs_addr[0] + 'h4;
data[0] = 'h10f;
msg_writew ( 0, addr, 1, data );
endtask // adc_init
task add_rule ( string rep_cap_id );
int idx;
idx = this.rules.add ( rep_cap_id );
write_rule ( idx );
endtask // add_rule
function int find_rule ( string rep_cap_id );
......@@ -242,50 +308,76 @@ class WrtdDev;
write_rule ( idx );
endtask // remove_rule
task set_rule ( string rep_cap_id, WrtdTstamp delay = null,
string src = "", string dst = "" );
task set_rule ( string rep_cap_id,
string src, string dst,
uint32_t delay_ns );
int idx;
idx = this.rules.set ( rep_cap_id, delay, src, dst );
//apply_rules ();
endtask // set_rule
/* -----\/----- EXCLUDED -----\/-----
task apply_rules ( );
int i, count, src_cpu, dst_cpu;
WrtdRule rule;
for ( i = 0; i < this.rules.collection_size(); i++ )
begin
if ( this.rules.is_free( i ) == 0 )
begin
src_cpu = find_channel ( this.rules.get_src ( i ), WRTD_DIR_INPUT );
if ( src_cpu < 0 )
src_cpu = find_alarm ( this.rules.get_src ( i ) );
if ( src_cpu < 0 )
src_cpu = rx_cpu;
end
end
endtask // apply_rules
-----/\----- EXCLUDED -----/\----- */
idx = this.rules.find ( rep_cap_id );
if ( idx == -1 )
$error ( "%s repeated capability ID cannot be set because it does not exist", rep_cap_id );
if ( this.rules.collection[idx].is_enabled ( ) )
$error ( "%s repeated capability ID cannot be set because it is enabled", rep_cap_id );
$cast ( rule, this.rules.collection[idx] );
rule.set_src ( src );
rule.set_dst ( dst );
rule.set_delay_ns ( delay_ns );
endtask // set_rule
task write_rule ( int idx );
int core, index;
int core, hash;
uint32_t addr;
uint32_t data[];
WrtdRule rule;
if ( idx < 0 )
$error ( "cannot write rule with negative index" );
core = this.rules.get_core ( idx );
index = this.rules.get_index (idx );
addr = this.roots[core].rules_addr + index * `WRTD_RULE_WORD_SIZE * 4;
data = this.rules.data_pack( idx );
$cast ( rule, this.rules.collection[idx] );
hash = wrtd_gen_hash ( rule.get_src() ) % this.rules.collection.size();
core = rule.get_core ( );
addr = this.roots[core].rules_addr + hash * `WRTD_RULE_WORD_SIZE * 4;
data = rule.data_pack ( );
msg_writew ( core, addr, `WRTD_RULE_WORD_SIZE, data );
endtask // write_rule
task enable_rule ( int idx );
this.rules.set_enable ( idx );
// TODO reconfigure
task enable_rule ( string rep_cap_id );
int idx;
string id;
uint32_t src_cpu;
WrtdRule rule;
idx = this.rules.find ( rep_cap_id );
if ( idx == -1 )
$error ( "%s repeated capability ID cannot be enabled because it does not exist", rep_cap_id );
$cast ( rule, this.rules.collection[idx] );
rule.set_enable ( );
// TODO, replace hard-coding
src_cpu = 0;
if ( this.free_rule_slots[src_cpu] == 0 )
$error ( "no more available free rule slots on CPU %d", src_cpu );
this.free_rule_slots[src_cpu] -= 1;
rule.set_dest_cpu ( `WRTD_DEST_CPU_LOCAL );
id = rule.get_dst ( );
if ( id == "ADCO1" )
rule.set_dest_ch ( 0 );
else
rule.set_dest_ch ( `WRTD_DEST_CH_NET );
write_rule ( idx );
endtask // enable_rule
task add_alarm ( string rep_cap_id );
......@@ -312,10 +404,10 @@ class WrtdDev;
if ( idx < 0 )
$error ( "cannot write alarm with negative index" );
core = this.alarms.get_core ( idx );
index = this.alarms.get_index (idx );
core = this.alarms.collection[idx].get_core ( );
index = this.alarms.collection[idx].get_index ();
addr = this.roots[core].alarms_addr + index * `WRTD_ALRM_WORD_SIZE * 4;
data = this.alarms.data_pack( idx );
data = this.alarms.collection[idx].data_pack( );
msg_writew ( core, addr, `WRTD_ALRM_WORD_SIZE, data );
endtask // write_alarm
......@@ -333,6 +425,6 @@ class WrtdDev;
join
endtask // update
endclass // WrtdDev
endclass // WrtdDrv
`endif // `ifndef __WRTD_DEV_INCLUDED
`endif // `ifndef __WRTD_DRV_INCLUDED
......@@ -27,11 +27,9 @@
`include "wrtd_definitions.svh"
typedef uint32_t repcap_data[];
virtual class WrtdRepCap;
protected string name;
protected string rep_cap_id;
protected WrtdId rep_cap_id;
protected int core;
protected int index;
protected int enabled;
......@@ -40,11 +38,12 @@ virtual class WrtdRepCap;
this.core = core;
this.index = index;
this.name = name;
this.rep_cap_id = new();
clear();
endfunction // new
function void clear ( );
this.rep_cap_id = "";
this.rep_cap_id.clear();
this.enabled = 0;
endfunction // clear
......@@ -66,19 +65,19 @@ virtual class WrtdRepCap;
endfunction // get_index
function string get_rep_cap_id ( );
return this.rep_cap_id;
return this.rep_cap_id.get();
endfunction // get_rep_cap_id
function void set_rep_cap_id ( string id );
this.rep_cap_id = id;
this.rep_cap_id.set( id );
endfunction // set_rep_cap_id
function int match ( string id );
return ( this.rep_cap_id.compare ( id ) == 0 );
return ( get_rep_cap_id().compare ( id ) == 0 );
endfunction // is_equal
function int is_free ( );
return ( this.rep_cap_id.len == 0 );
return this.rep_cap_id.is_empty();
endfunction // is_free
function int is_enabled ( );
......@@ -93,19 +92,19 @@ virtual class WrtdRepCap;
this.enabled = 0;
endfunction // set_disable
pure virtual function repcap_data data_pack ( );
pure virtual function wrtd_data data_pack ( );
pure virtual function void data_unpack ( repcap_data data );
pure virtual function void data_unpack ( wrtd_data data );
endclass //WrtdRepCap
virtual class WrtdRepCapCollection;
class WrtdRepCapCollection;
protected string name;
protected WrtdRepCap collection[];
WrtdRepCap collection[$];
function new ( int size, string name );
this.name = name;
this.collection = new[size];
function new ( string name );
this.name = name;
this.collection.delete();
endfunction // new
task mdisplay ( string str );
......@@ -118,49 +117,12 @@ virtual class WrtdRepCapCollection;
endtask // mdisplay
function void validate_id ( string rep_cap_id );
if ( rep_cap_id.len > `WRTD_ID_LEN )
if ( rep_cap_id.len() > `WRTD_ID_LEN )
$error ( "repeated capability name '%s' is too long", rep_cap_id );
if ( rep_cap_id.len == 0 )
if ( rep_cap_id.len() == 0 )
$error ( "repeated capability name is null" );
endfunction // validate_id
function void init_element ( int idx, int core, int rule );
this.collection[idx] = new ( core, rule, this.name );
endfunction // init_element
function int collection_size( );
return this.collection.size();
endfunction // collection_size
function int is_free ( int idx );
return ( this.collection[idx].is_free() );
endfunction // is_free
function int get_core ( int idx );
return this.collection[idx].get_core();
endfunction // get_core
function int get_index ( int idx );
return this.collection[idx].get_index();
endfunction // get_index
function void set_enable ( int idx );
this.collection[idx].set_enable();
endfunction // set_enable
function void set_disable (int idx );
this.collection[idx].set_disable();
endfunction // set_disable
function repcap_data data_pack ( int idx );
return this.collection[idx].data_pack();
endfunction // data_pack
function void data_unpack ( int idx, repcap_data data );
this.collection[idx].data_unpack ( data );
endfunction // data_unpack
function int add ( string rep_cap_id );
int i, idx;
......@@ -168,19 +130,22 @@ virtual class WrtdRepCapCollection;
idx = -1;
for ( i = 0; i < collection_size(); i++ )
for ( i = 0; i < this.collection.size(); i++ )
begin
if ( this.collection[i].match ( rep_cap_id ) )
$error ( "%s repeated capability ID already exists", rep_cap_id );
begin
$error ( "'%s' repeated capability ID already exists", rep_cap_id );
return -1;
end
if ( idx == -1 && this.collection[i].is_free() )
idx = i;
end
if ( idx == -1 )
$error ( "cannot add %s repeated capability, no space available", rep_cap_id );
this.collection[idx].set_rep_cap_id ( rep_cap_id );
$error ( "cannot add '%s' repeated capability, no space available", rep_cap_id );
else
this.collection[idx].set_rep_cap_id ( rep_cap_id );
return idx;
endfunction // add
......@@ -190,7 +155,7 @@ virtual class WrtdRepCapCollection;
validate_id ( rep_cap_id );
for ( i = 0; i < collection_size(); i++ )
for ( i = 0; i < this.collection.size(); i++ )
if ( this.collection[i].match ( rep_cap_id ) )
return i;
......@@ -203,17 +168,19 @@ virtual class WrtdRepCapCollection;
idx = find ( rep_cap_id );
if ( idx == -1 )
$error ( "%s repeated capability ID cannot be removed because it does not exist", rep_cap_id );
begin
$error ( "%s repeated capability ID cannot be removed because it does not exist", rep_cap_id );
return idx;
end
if ( this.collection[idx].is_enabled() )
if ( this.collection[idx].is_enabled ( ) )
$error ( "%s repeated capability ID cannot be removed because it is enabled", rep_cap_id );
this.collection[idx].clear();
else
this.collection[idx].clear();
return idx;
endfunction // remove
endclass // WrtdRepCapCollection
`endif // `ifndef __WRTD_REPCAP_INCLUDED
......@@ -4,7 +4,7 @@
// https://ohwr.org/projects/wrtd
//------------------------------------------------------------------------------
//
// unit name: WrtdRule, WrtdRuleCollection
// unit name: WrtdRule
//
// description: A SystemVerilog Class for a WRTD "rule" repeated capability
//
......@@ -28,74 +28,149 @@
`include "wrtd_rep_cap.svh"
class WrtdRule extends WrtdRepCap;
protected string src;
protected string dst;
protected WrtdTstamp delay;
protected int send_late;
protected WrtdId src;
protected WrtdId dst;
protected byte unsigned dest_cpu;
protected byte unsigned dest_ch;
protected byte unsigned send_late;
protected uint32_t repeat_count;
protected uint32_t delay_ns;
protected uint32_t hold_off_ns;
protected uint32_t resync_period_ns;
protected uint32_t resync_delay_ns;
int hash_chain;
protected uint32_t rx_events;
protected WrtdTstamp rx_last;
protected uint32_t tx_events;
protected WrtdTstamp tx_last;
protected uint32_t lat_max_ns;
protected uint32_t lat_lo_ns;
protected uint32_t lat_hi_ns;
protected uint32_t lat_nbr;
protected uint32_t miss_holdoff;
protected uint32_t miss_late;
protected WrtdTstamp miss_last;
protected WrtdTstamp hold_off;
protected uint32_t seq;
function new ( int core, int index, string name = "" );
super.new ( core, index, name );
this.delay = new ();
this.src = new();
this.dst = new();
this.rx_last = new();
this.tx_last = new();
this.miss_last = new();
this.hold_off = new();
clear();
endfunction // new
function void clear ( );
super.clear();
this.delay.zero();
this.src = "";
this.dst = "";
this.send_late = 1;
this.src.clear();
this.dst.clear();
this.rx_last.zero();
this.tx_last.zero();
this.miss_last.zero();
this.hold_off.zero();
this.dest_cpu = 0;
this.dest_ch = 0;
this.send_late = 1;
this.repeat_count = 0;
this.delay_ns = 0;
this.hold_off_ns = 0;
this.resync_period_ns = 0;
this.resync_delay_ns = 0;
this.hash_chain = -1;
endfunction // clear
function repcap_data data_pack ( );
function wrtd_data data_pack ( );
wrtd_data ret = new[`WRTD_RULE_WORD_SIZE];
ret[0:3] = this.rep_cap_id.data_pack();
ret[4:7] = this.src.data_pack();
ret[8:11] = this.dst.data_pack();
ret[12] = this.dest_cpu << 0;
ret[12] |= this.dest_ch << 8;
ret[12] |= this.enabled << 16;
ret[12] |= this.send_late << 24;
ret[13] = this.repeat_count;
ret[14] = this.delay_ns;
ret[15] = this.hold_off_ns;
ret[16] = this.resync_period_ns;
ret[17] = this.resync_delay_ns;
ret[18] = this.hash_chain;
ret[19] = this.rx_events;
ret[20:22] = this.rx_last.data_pack();
ret[23] = this.tx_events;
ret[24:26] = this.tx_last.data_pack();
ret[27] = this.lat_max_ns;
ret[28] = this.lat_lo_ns;
ret[29] = this.lat_hi_ns;
ret[30] = this.lat_nbr;
ret[31] = this.miss_holdoff;
ret[32] = this.miss_late;
ret[33:35] = this.miss_last.data_pack();
ret[36:38] = this.hold_off.data_pack();
ret[39] = this.seq;
return ret;
endfunction // data_pack
function void data_unpack ( repcap_data data );
function void data_unpack ( wrtd_data data );
this.rep_cap_id.data_unpack ( data[0:3] );
this.src.data_unpack (data[4:7] );
this.dst.data_unpack( data[8:11] );
this.dest_cpu = ( data[12] & 'h000000ff ) >> 0;
this.dest_ch = ( data[12] & 'h0000ff00 ) >> 8;
this.enabled = ( data[12] & 'h00ff0000 ) >> 16;
this.send_late = ( data[12] & 'hff000000 ) >> 24;
this.repeat_count = data[13];
this.delay_ns = data[14];
this.hold_off_ns = data[15];
this.resync_period_ns = data[16];
this.resync_delay_ns = data[17];
this.hash_chain = data[18];
this.rx_events = data[19];
this.rx_last.data_unpack ( data[20:22] );
this.tx_events = data[23];
this.tx_last.data_unpack ( data[24:26] );
this.lat_max_ns = data[27];
this.lat_lo_ns = data[28];
this.lat_hi_ns = data[29];
this.lat_nbr = data[30];
this.miss_holdoff = data[31];
this.miss_late = data[32];
this.miss_last.data_unpack ( data[33:35] );
this.hold_off.data_unpack ( data[36:38] );
this.seq = data[39];
endfunction // data_unpack
function string get_src ( );
return this.src;
return this.src.get();
endfunction // get_src
function void set ( WrtdTstamp delay, string src, string dst );
if ( delay != null )
this.delay = delay;
else
this.delay.zero();
this.src = src;
this.dst = dst;
endfunction // set
function string get_dst ( );
return this.dst.get();
endfunction // get_dst
endclass //WrtdRule
class WrtdRuleCollection extends WrtdRepCapCollection;
protected WrtdRule collection[];
function new ( int size, string name );
super.new ( size, name );
endfunction // new
function int set ( string rep_cap_id, WrtdTstamp delay,
string src, string dst );
int idx;
function void set_src ( string src );
this.src.set ( src );
endfunction // set_src
idx = find ( rep_cap_id );
function void set_dst ( string dst );
this.dst.set ( dst );
endfunction // set_src
if ( idx == -1 )
$error ( "%s repeated capability ID cannot be set because it does not exist", rep_cap_id );
function void set_dest_cpu ( uint32_t dest_cpu );
this.dest_cpu = dest_cpu;
endfunction // set_dest_cpu
if ( this.collection[idx].is_enabled() )
$error ( "%s repeated capability ID cannot be set because it is enabled", rep_cap_id );
function void set_dest_ch ( uint32_t dest_ch );
this.dest_ch = dest_ch;
endfunction // set_dest_ch
this.collection[idx].set ( delay, src, dst );
function void set_delay_ns ( uint32_t delay_ns );
this.delay_ns = delay_ns;
endfunction // set_delay_ns
return idx;
endfunction // set
function string get_src ( int idx );
return this.collection[idx].get_src();
endfunction // get_src
endclass // WrtdRuleCollection
endclass //WrtdRule
`endif // `ifndef __WRTD_RULE_INCLUDED
......@@ -76,7 +76,8 @@ module dut_env
wrtd_ref_spec150t_adc #
(
.g_SIMULATION (1)
.g_SIMULATION (1),
.g_WRPC_INITF ("../../../dependencies/wr-cores/bin/wrpc/wrc_phy8_sim.bram")
)
DUT
(
......
......@@ -25,29 +25,30 @@
`timescale 1ns/1ps
`include "gn4124_bfm.svh"
`include "wrtd_dev.svh"
`include "wrtd_driver.svh"
`include "fmc_adc_100Ms_csr.v"
`include "fmc_adc_alt_trigout.v"
`define ADC_CSR_BASE 'h5000
module main;
IGN4124PCIMaster hostA ();
//IGN4124PCIMaster hostB ();
IGN4124PCIMaster hostB ();
reg duta_ext_trig, dutb_ext_trig;
wire[2:0] duta_acq_state, dutb_acq_state;
dut_env DUTA (hostA, duta_acq_state, a2b_txp, a2b_txn, a2b_rxp, a2b_rxn, duta_ext_trig);
//dut_env DUTB (hostB, dutb_acq_state, a2b_rxp, a2b_rxn, a2b_txp, a2b_txn, dutb_ext_trig);
dut_env DUTB (hostB, dutb_acq_state, a2b_rxp, a2b_rxn, a2b_txp, a2b_txn, dutb_ext_trig);
IMockTurtleIRQ MtIrqMonitorA (`MT_ATTACH_IRQ(DUTA.DUT.cmp_mock_turtle));
//IMockTurtleIRQ MtIrqMonitorB (`MT_ATTACH_IRQ(DUTB.DUT.cmp_mock_turtle));
IMockTurtleIRQ MtIrqMonitorB (`MT_ATTACH_IRQ(DUTB.DUT.cmp_mock_turtle));
CBusAccessor accA, accB;
WrtdDev devA, devB;
WrtdDrv devA, devB;
const uint64_t MT_BASE = 'h0002_0000;
......@@ -59,86 +60,156 @@ module main;
duta_ext_trig <= 1'b0;
wait (hostA.ready == 1'b1);// && (hostB.ready == 1'b1));
wait ((hostA.ready == 1'b1) && (hostB.ready == 1'b1));
accA = hostA.get_accessor();
//accB = hostB.get_accessor();
fork
begin
accA = hostA.get_accessor();
accA.set_default_xfer_size(4);
devA = new (accA, MT_BASE, MtIrqMonitorA, "DUT:A");
devA.init();
devA.add_rule ( "rule0" );
devA.set_rule ( "rule0", "ADCI5", "NET0", 0 );
devA.enable_rule ( "rule0" );
// Config DUTA to trigger on external trigger and get 64 samples
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_PRE_SAMPLES, 'h00);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_POST_SAMPLES, 'h40);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_SHOTS, 'h01);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH1_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH2_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH3_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH4_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH1_SAT, 'h7fff);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH2_SAT, 'h7fff);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH3_SAT, 'h7fff);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH4_SAT, 'h7fff);
val = (1'b1 << `FMC_ADC_100MS_CSR_TRIG_EN_EXT_OFFSET);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_TRIG_EN, val);
expected = 'h39;
accA.read(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_STA, val);
if (val != expected)
$fatal (1, "ADC status error (got 0x%8x, expected 0x%8x).", val, expected);
$display ("<%t> DUTA ADC configured and armed", $realtime);
wait (duta_acq_state == 1);
end
begin
accB = hostB.get_accessor();
accB.set_default_xfer_size(4);
devB = new (accB, MT_BASE, MtIrqMonitorB, "DUT:B");
devB.init();
devB.add_rule ( "rule0" );
devB.set_rule ( "rule0", "NET0", "ADCO1", 100000 );
devB.enable_rule ( "rule0" );
// Config DUTB to trigger on WRTD and get 64 samples
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_PRE_SAMPLES, 'h00);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_POST_SAMPLES, 'h40);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_SHOTS, 'h01);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH1_GAIN, 'h8000);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH2_GAIN, 'h8000);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH3_GAIN, 'h8000);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH4_GAIN, 'h8000);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH1_SAT, 'h7fff);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH2_SAT, 'h7fff);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH3_SAT, 'h7fff);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH4_SAT, 'h7fff);
val = (1'b1 << `FMC_ADC_100MS_CSR_TRIG_EN_ALT_TIME_OFFSET);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_TRIG_EN, val);
expected = 'h39;
accB.read(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_STA, val);
if (val != expected)
$fatal (1, "ADC status error (got 0x%8x, expected 0x%8x).", val, expected);
$display ("<%t> DUTB ADC configured and armed", $realtime);
wait (dutb_acq_state == 1);
end
join
accA.set_default_xfer_size(4);
//accB.set_default_xfer_size(4);
#50us;
devA = new (accA, MT_BASE, MtIrqMonitorA, "DUT:A");
//devB = new (accB, MT_BASE, MtIrqMonitorB, "DUT:B");
$display("<%t> DUTB START ACQ 1", $realtime);
accB.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CTL, 'h00000001); // FSM start
$display("<%t> DUTA START ACQ 1", $realtime);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CTL, 'h00000001); // FSM start
// Config DUTA to trigger on external trigger get 64 samples
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_PRE_SAMPLES, 'h00);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_POST_SAMPLES, 'h40);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_SHOTS, 'h01);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH1_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH2_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH3_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH4_GAIN, 'h8000);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH1_SAT, 'h7fff);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH2_SAT, 'h7fff);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH3_SAT, 'h7fff);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CH4_SAT, 'h7fff);
#5us;
duta_ext_trig <= 1'b1;
#10ns;
duta_ext_trig <= 1'b0;
val = (1'b1 << `FMC_ADC_100MS_CSR_TRIG_EN_EXT_OFFSET);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_TRIG_EN, val);
fork
begin
wait (duta_acq_state == 1);
$display("<%t> DUTA END ACQ 1", $realtime);
expected = 'h39;
accA.read(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_STA, val);
if (val != expected)
$fatal (1, "ADC status error (got 0x%8x, expected 0x%8x).", val, expected);
accA.read(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_TRIG_POS, val);
$display ("<%t> DUTA TRIG POSITION %.8x", $realtime, val);
$display ("<%t> DUTA ADC configured and armed", $realtime);
wait (duta_acq_state == 1);
// DMA transfer
accA.write('h2008, val); // dma start addr
#500us;
accA.write('h200C, 'h00001000); // host addr
accA.write('h2010, 'h00000000);
$display("<%t> START ACQ 1", $realtime);
accA.write(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_CTL, 'h00000001); // FSM start
accA.write('h2014, 'h00000100); // len << 2
#5us;
duta_ext_trig <= 1'b1;
#10ns;
duta_ext_trig <= 1'b0;
accA.write('h2018, 'h00000000); // next
accA.write('h201C, 'h00000000);
wait (duta_acq_state == 1);
$display("<%t> END ACQ 1", $realtime);
accA.write('h2020, 'h00000000); // attrib: pcie -> host
accA.read(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_TRIG_POS, val);
$display ("<%t> TRIG POSITION %.8x", $realtime, val);
accA.write('h2000, 'h00000001); // xfer start
// DMA transfer
accA.write('h2008, val); // dma start addr
wait (DUTA.DUT.dma_irq[0] == 1);
$display("<%t> DUTA END DMA 1", $realtime);
end
accA.write('h200C, 'h00001000); // host addr
accA.write('h2010, 'h00000000);
begin
wait (dutb_acq_state == 1);
$display("<%t> DUTB END ACQ 1", $realtime);
accA.write('h2014, 'h00000100); // len << 2
accB.read(`ADC_CSR_BASE + `ADDR_FMC_ADC_100MS_CSR_TRIG_POS, val);
$display ("<%t> DUTB TRIG POSITION %.8x", $realtime, val);
accA.write('h2018, 'h00000000); // next
accA.write('h201C, 'h00000000);
// DMA transfer
accB.write('h2008, val); // dma start addr
accA.write('h2020, 'h00000000); // attrib: pcie -> host
accB.write('h200C, 'h00001000); // host addr
accB.write('h2010, 'h00000000);
accA.write('h2000, 'h00000001); // xfer start
accB.write('h2014, 'h00000100); // len << 2
wait (DUTA.DUT.dma_irq[0] == 1);
$display("<%t> END DMA 1", $realtime);
accB.write('h2018, 'h00000000); // next
accB.write('h201C, 'h00000000);
accB.write('h2020, 'h00000000); // attrib: pcie -> host
accB.write('h2000, 'h00000001); // xfer start
wait (DUTB.DUT.dma_irq[0] == 1);
$display("<%t> DUTB END DMA 1", $realtime);
end
fork
devA.init();
//devB.init();
join
end
initial begin
forever begin
devA.update();
//devB.update();
if ( ( devA != null ) && ( devA.ready ) )
devA.update();
if ( ( devB != null ) && ( devB.ready ) )
devB.update();
#1us;
end
end
initial begin
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment