Tuesday, May 22, 2018

SystemVerilog Interfaces - An unpleasant construct

I haven't written in a while, but I just can't hold myself back. I have spent days writing some fancy little test bench code and because I can't fight my own nature, I just had to try and do it with the most advanced techniques possible - with the most elegant solutions.

So I tried to use interfaces... and it has proven to be a colossal failure.

1. Use the "correct" construct in each situation
2. Minimal replication, both of signals and of code
3. Use arrays

I have a couple modules that I want to test, testing each separately and then together.
I have interconnect between these modules.
I have test bench components that can optionally be used to drive or receive one side of the interconnect. (An interface has signals that goes between a DRIVER and a RECEIVER, I have test bench components that can emulate the DRIVER and RECEIVER.)

An interface that declares a bunch of signals to connect two modules (DRIVER, RECEIVER) should have no clocking blocks - why? Because clocking blocks are only for test bench logic in the interface and would create multiple drive issues when you connect both a module and have a clocking block that can also drive the same signals. (Workarounds of course are possible, but trying to avoid those.)

First problem:
Cannot declare a clocking block in a modport to make it only exist within the context of a test bench component connecting to the modport (or not exist when connecting to a module). We are stuck creating additional interface components to attach on top:

I create an interface which declares the signals and the standard modports. Now this interface (SIGNAL_INTF) has no procedural test bench code.

I then create interfaces called DRIVER_INTF and RECEIVER_INTF where I do not declare any signals - and I just use them to attach to SIGNAL_INTF as a DRIVER or RECEIVER. These blocks get the interface as a port.

Now I have arrays of interfaces that I want to use in my code. So I write code to index into the interfaces.

Second problem:
always_comb begin
  sum = 0;
  for (int i = 0; i < NUM; i++)
   sum += intf[i].value;

Nope - this syntax is not allowed. Can't index into an interface array with a non-const expression. 'i' is not a constant. Why? Because unlike a regular array an interface array is non-homogeneous as it can have different types stored within it - has to do with defparams. Reference to discussion:

And of course interfaces are not strongly typed (did I use that terminology wrong?), and it doesn't appear that they can be. For example, the simulation tool I'm using doesn't bother to check modports at all. Remove modports - same result, change interface name to generic 'interface' - same results. Interfaces are connected at elaboration time, but even then, they let you connect anything up. Makes modports useless. Because of this, parameterizing interfaces within a port list is unnecessary - but also not compilable. I wish it was, as I like to use strong typing. It helps me avoid bugs.

Third problem (not really a problem as modports don't really matter):
No syntax support for parametrizable interface on a port list
No way to pass in modport name for interface instance array in an instantiation connection

Yep, there are workarounds for all of this, but if you are like me, and you wish to code in the most advanced and "proper" way possible, I would avoid using interfaces too much. A little bit might be OK, but they are not well thought out and definitely not language ready.

Saturday, January 16, 2016

Converting a 2 Clock Read->Out FIFO to First Word Fall Through

There's a bunch of pages describing how to write a First Word Fall Through FIFO, see http://www.billauer.co.il/reg_fifo.html

But Neither of these deal with how to write ] an FWFT FIFO on top of a FIFO that uses Xilinx's output registers. Why is this different?

In a simple one clock read_enable->data_out scheme, you can pre buffer the first data out, and then use the first read acknowledge to trigger the next read enable. This gives you back to back results out of the FWFT FIFO.

In a two clock read_enable->data_out scheme, you must wait an additional clock for your data, and that means that an FWFT implementation must perform 2 read enables without waiting for read acknowledges. You run the risk of overwriting the first read data with the second data. This makes it a bit tricky to convert such a FIFO to FWFT.

And here's my solution:
Use clock enables.
Sounds simple, and it is. Using the clock enables allows you to read data out of the FIFO, and then control the FIFO logic to stop reading. Why not just use the read_enables?
1. Can't stop read enables mid fetch without clock enables. Must stop second read enable from overwriting output data of the first read enable.
2. Timing. My FWFT logic still makes 250 MHz + timing on a Virtex 6 and Virtex 7. (My Vivado project uses default synthesis and implementation options.)

This is not as trivial as it sounds. You must carefully control both stages of clock enables. My own FIFO implementation can optionally implement arbitrarily wide and deep RAMB18/36E1 blocks to create it's FIFO memory. Since I support this, I also make sure to support the full range of clock enables going into the RAMB primitives. Xilinx was very intelligent in the RAMB design, and b/c of their foresight, we get to control each stage of output separately. As long as you can control both stages of data properly, you can implement an FWFT with an almost trivial amount of ease.


Friday, June 19, 2015

CentOS 7 / RHEL 7 - XDMCP

If you're trying to get XDMCP working on CentOS 7 or RHEL 7, you must switch lightdm and xfce b/c GDM and Gnome (and I assume KDE) require direct hardware access. I kept getting a black screen when trying to connect. Switching to lightdm resolved this.

This page explains it best.

Friday, December 26, 2014

Synplify and CDC files - Compiler Directives Constraint files

Synplify Compiler Directives Constraint (CDC) files:

Here's the missing links:

1. To refer to nets you must use the | syntax:
define_directive {n:my_mod|my_net} {syn_keep} {1}

2. Remember that CDC files are at compile time. No hierarchies here.
Don't do this:
define_directive {n:my_mod.my_submod_inst|my_net} {syn_keep} {1}
Do this:
define_directive {n:my_submod|my_net} {syn_keep} {1}

This is all you really have to know.

Friday, October 11, 2013

AES (Advanced Encryption Standard)

I'm not gonna teach you how to perform AES encryption or decryption. So if you are looking for that, then you can turn right around and go. But I will tell you some interesting things that I learned about AES. These may help you in conjunction with other tutorials.

1. Multiply is not multiply. You will need a function to perform gmul. It is multiply in a Galois field. I don't really know much about what a Galois field is, but it is an alternate universe when it comes to mathematics. So when they say multiply, this is what they mean.

2. Add and subtract are actually XOR. Wherever it says subtract it is the same operation as add. Realize that every step of AES (key generation, adding round keys, substitute bytes, shifting rown, and mixing columns) require Galois operations. Multiply, add / subtract.

3. Decryption is harder than encryption. Yes, that sounds weird, but it's true. What I mean is that to perform encryption you just need the key to begin with. You can actually generate the keys on the fly. To perform decryption, you MUST perform full key expansion to get the final key. They you can work backwards on the fly. Also decryption's inverse mix columns step requires 4 multiply look-up tables as opposed to 2 for encryption's mix columns step.

4. AES 256 is easier to implement than AES 192. The biggest difficulty is on the fly key generation. If you want to generate AES 128 on the fly, then it is the same sequence for each round of encryption. For 256, it is the same round every 2 times. For 192, it is different. B/c each round of key expansion produces 192 bits (24 bytes) and each round of encryption uses 16 bytes, you have to loop through 1.5 rounds of encryption before starting a new line of key expansion. Of course AES 256 requires more flops.

Thursday, August 22, 2013

SystemVerilog array of objects initialization

So I'm updating one of my testbenches and I want to create an array of objects. For example:
A_class a_instance[num];

I also want to pass these objects to modules and other created objects.

B_class b_instance = new (a_instance[0]);
C_mod c_modinst (.a(a_instance[0]));

The biggest issue is that a_instance isn't yet initialized.

If you try and initialize with

initial begin
for(genvar i = 0; i &lt; num; i++) a_instance[i] = new();

This won't work. Has something to do with object and module creation running before initial lines. The initial statement is too late, a null object was passed in and that's what the object and modules will have.

When I was passing a non array, it would work b/c the declaration included an assignment:
A_class a_instance = new();
but you can't call new on an array.

Here's what appears to work:
A_class a_instance = '{num{A_class::create()}};

This surprised me as I am using replication. It looks like each instance points to its own object. This resolves the problem. Now on the declaration line, I can initialize the objects. Passing the objects around works well now.

Update from Idan's comment:
A_class a_instance = '{default:A_class::create()};

This uses the default syntax for filling in an array. Much nicer than the replication mechanism.

About the create function: SystemVerilog doesn't allow you to call new on a class type so I use a create function instead:

class a;
 function new();
  $display("creating a");
 static function a create();
  class a_inst;
  a_inst = new();
  return a_inst;

I believe others refer to this as a factory create function or something like that. Now creating a is as easy as calling a::create().

Saturday, June 8, 2013

Installing rdesktop with user privileges (Red Hat EL 5.5)

Recently I came across a challenge of installing remote desktop without root privileges. Here is the information. Kudos to: http://www.nordugrid.org/documents/rpm_for_everybody.html for showing me how to do this.

In my home directory I performed these steps:
# make rpm database
mkdir rpmdb
rpmdb --initdb --dbpath ~/rpmdb/
# prepare folders
mkdir -p rpmtop/RPMS/i386
mkdir rpmtop/SRPMS
mkdir rpmtop/SOURCES
mkdir rpmtop/BUILD
mkdir rpmtop/SPECS
mkdir rpmtmp
echo `%_dbpath /home//rpmdb' | cat >> ~/.rpmmacros 
echo '%_topdir /home//rpmtop' | cat >> ~/.rpmmacros 
echo '%_tmppath /home//rpmtmp' | cat >> ~/.rpmmacros 
# copy system installed rpm list (must do this to meet dependency requirements of desired apps)
cp /var/lib/rpm/* rpmdb/.
# build rdesktop
rpmbuild --rebuild rdesktop-1.6.0-3.src.rpm
# install rdesktop
rpm -ivh rpmtop/RPMS/x86_64/rdesktop-1.6.0-3.x86_64.rpm
# since rdesktop uses keymaps by default from /usr/local, and since rdesktop isn't installed there, we will cheat by creating a link to 'user' available keymaps
ln -s usr/share/rdesktop/ ~/.rdesktop

This will give you:
which works wonderfully well for connecting to remote windows systems.