Tuesday, November 25, 2014

The devices that we verify often have multiple instances of a certain register type. These registers are instantiated in a regular structure inside the design. In our tests, we want to be able to access each of them.

In vr_ad, accessing a register is typically done using the write/read_reg family of macros. These macros encapsulate the very flexible access mechanisms that the package provides into one convenient call. For unique registers, they just work, without having to pass in any additional options, but things get a bit more involved if the register type we are trying to access is instantiated multiple times. There are multiple scenarios where this can be the case. Let's have a look at them!

The first scenario that comes to mind is when there are multiple instances of the same register inside a register file. Let's take the example from the last post, with the graphics processing device:

Trying to use write_reg like this will result in an error message, stating that there are multiple TRIANGLE registers inside the address map. It's impossible for the macro to know exactly which instance we want. We can solve this ambiguity in two ways.

The first way is by using the less known operation generate block argument to the macro (which is optional). We get the exact instance of the model register we want and pass that in as the static_item:

In this case, we don't have to pass a static_item anymore, but we have to cast the static triangle to be able to access the TRIANGLE subtype's fields. In both cases, though, we have to write quite a bit of code just to access the register we want.

Instead of always getting the static_item ourselves and using that, why not encapsulate the whole operation inside a macro of our own? Since macros are anyway used to access vr_ad registers, it shouldn't cause any confusion for anybody. What we need is an extra macro argument, so our macro should look something like this: write_triangle_reg <inst'idx> <reg'exp> <reg_gen'block> (let's ignore the <op_gen'block> argument for simplicity).

Such a macro would just fetch the appropriate static_item based on the <inst'idx> argument and forward the other arguments to write_reg. This is how such a macro would look like:

Now this is all fine and dandy, but what if we also want to read the registers? We would need a second macro, whose expansion would be very similar to the first one's. We could generalize the macro body code inside a function that can return the result for both access operations. The vr_ad package defines such functions inside the global singleton, so if it's good enough for Cadence, then it's good enough for us:

This function can take any access operation (write_reg, read_reg or write_reg_fields - a lesser know brother of the two), together with the macro arguments and return the macro body. Declaring the macros just means calling this function with the appropriate arguments:

The *_triangle_reg macros won't work on these registers so we're back to where we started. It would be very silly to create a new macro that can handle only circles, because that would become really unmaintainable if we were to add more and more shapes. What we need is a macro that can work with any register, regardless of type.

We already have the information about the register's kind inside the register itself. We just need to use that when calling get_regs_by_kind(...) to get the static register. We'll call this new macro write_graphics_reg and define a new function that implements all three versions of it:

Let's look at a different scenario now. Let's assume that our DUT is composed of multiple slices, each of which is able to do some graphics processing independently of the others. An individual slice can only process one triangle and one circle, but there are many such slices. In this case, the register definitions would look like this:

As before, we can access a certain triangle or circle by getting the appropriate register instance from the address map and using that with write_reg, like we did in the previous scenario. What we can also do is get a handle to the appropriate register file and use that as the static_item:

Things start to get fun when the design contains a mixture of the two cases we've looked at above. Our registers are organized in slices, but within one slice some registers may be instantiated multiple times:

This means that our macro has to be able to handle both the register file and register indices. We can only process one square at a time, though, so in that case it doesn't make sense to pass in a register index (seeing as how there is only one instance per register file). The register index argument must therefore be optional. Starting top-down, this is how the definition of the write_graphics_reg macro would look like:

As we've seen, by encapsulating the write/read_reg macros inside our own we can easily select the register instance that we want to access. We save a lot of tedious typing and duplicated code to get the appropriate static_item every time. We pay a small price when using macros though, as it becomes more difficult to track down syntax errors, but with proper documentation the disadvantages can be reduced. For more examples and detailed code, check out the SourceForge repository.

If your next project involves a lot registers with multiple instances, why not try this approach out? See you next time!

Monday, November 17, 2014

On my current project, I had an issue with my register definitions. Quite a few of my DUT's registers where just instances of the same register type. My vr_ad register definitions were generated by a script, based on the specification, a flow that I'm pretty sure is very similar to what most of you also have. Instead of generating a nice regular structure, this script created a separate type for each register instance. What resulted was a flattened structure where I'd, for example, get one instance each of registers SOME_REG0, SOME_REG1, SOME_REG2, instead of three instances of SOME_REG. I was lucky enough to be able to (partly) change the definitions by patching them by hand.

Someone on StackOverflow had the same problem, but didn't have the luxury of being able to fix it like I did. They weren't allowed to touch the code as I'm guessing it probably belonged to a different team. They probably also had a lot of legacy code that was using those flattened register definitions. This made me want to do an experimental post on how to best cope with such an issue.

Naturally the best thing to do is to fix the underlying problem of the registers getting flattened, but that might not be possible, so let's look at how to fix the symptoms.

To be able to do any kind of serious modeling, we need to be able to program generically. We can't (easily) do this if each register is an own type. I've tried to think of how to best handle this from a maintainability point of view. As a bonus requirement, we'd also like it that when the register definitions do get fixed (i.e. the generation flow gets updated) we have to make as few changes as possible to the modeling code.

Enough with the stories, let's get our hands dirty. As always, we'll start small, but think big. We'll go through a few iterations, look at where we're lacking and gradually refine our approach.

Let's say we have a device that can operate with shapes. Part of its functionality involves doing stuff with triangles. It can process multiple triangles at the same time, where each triangle is described by a register containing the lengths of its sides. Our DUT does computations on the triangles, based on these values. For example, it can compute the areas of the triangles. We want to check that what the DUT writes out is correct so we need to model these computations.

We have a trusty script that can generate the register definitions from the specification (maybe an XML file). This script isn't very well written and it doesn't know that all three TRIANGLE registers are just the same register instantiated 3 times (i.e. a regular structure), or maybe the information got lost in the XML somehow. This is what we get for our register definitions:

We can immediately see a problem with this approach. We've implemented the formula in three different places. This means that should something change, we have three places to fix. Now, Heron's formula changing is a pretty unlikely event, but should we have a different computation to perform here the discussion stands.

What we can do is extract the part that computes the actual area as an own method, that takes the three sides as its arguments:

At least this way we've centralized the computation part to one location. The number of such methods will grow linearly, though, with the number of TRIANGLE registers. This means that for n triangles we'll need n methods to compute the areas.

Let's add a new requirement: our DUT is also able to compute which triangle is the largest and we need to model that too. We can define a new method to do that based on the areas:

In this method, the number of calls to get_triangleX_area() also grows with the number of triangles. Moreover, if we want to be able to find out which triangle is the smallest, the method for that would have to look like this:

Pretty much the same as largest(), isn't it? In this setup, adding a single triangle would require adding a new method for the area and changing two others. That's not very maintainable. We can use the same trick we did for the area computation and pull out computing the list of areas to it's own method, while simplifying the largest() and smallest() methods:

Now we only need to update the get_triangle_areas() method when adding a new triangle. Not much of an improvement, but every little thing counts when you're potentially dealing with a large number of triangles.

While we may have things sorted out for areas, we get a new requirement. Our DUT can also compute perimeters and tell us which triangle is the longest and which one is the shortest. This means we'll need to add a similar set of methods to handle this aspect, based on the examples from above:

Adding just one measly triangle is starting to become a real pain. What would be awesome is being able to just add one line of code every time a new triangle gets added and be done with it. Well, thanks to our good friends, the macros, this is possible.

What we notice is that the code is very regular. Aside from the indices, the method bodies look remarkably similar. This means that for the area aspect we can create the following macro:

We could define a similar macro for the perimeter aspect (I won't show it here). While we have made adding new triangles easier, we've also shot ourselves in the foot. Excessive use of macros is a code smell because it can be very difficult to understand what code gets expanded in the background. Also, it makes the code more difficult to refactor, since we can't rely on fancy IDE features.

If we analyze the code up now we see that one of our main problems is that each triangle is stored in an individual field. This means that there's no way to access a triangle from a method by just passing in the index of the triangle (0, 1, 2, etc.). If we could do this, we could get rid of all our get_triangleX_area() methods.

A way of doing this is using the reflection API. Reflection allows us, among others, to get a field of a struct by using only the name of that field, specified as a string. In our case, we know that our register file contains fields named triangle0, triangle1, triangle2, etc. We can use the reflection API to extract the field that contains contains the appropriate index as its suffix:

The way to use the reflection API is to get the representation of our register file from the rf_manager singleton. What we'll end up with is a struct of type rf_struct that understands what fields, methods, etc. the register file has. Out of this we can extract a representation of the field for the triangle that interests us, of type rf_field. Based on this field we can construct our return value. How exactly this happens is explained in the documentation and in this excellent post from the Specman R&D team. Have a look at those resources for more details on how to use the reflection interface.

After we've gotten an instance of our desired register, we can use this to compute the area. We can do away with the get_triangleX_area() methods and replace them with one get_triangle_area_by_index(...) method:

Because the return value of get_triangle_reg(...) is of type vr_ad_reg, we can't reference the SIDEx fields directly (as these are defined under when subtypes). We can't cast the value to any of these subtypes, because we would need n cast statements (the very thing we want to avoid). We can use the same method as before to get the values of the sides via the reflection interface. The resulting code isn't pretty, but it works. Can we do better, though?

Of course we can! An essential observation to make here is that all triangle register types contain the same fields, whether they are of type TRIANGLE0 or TRIANGLE1 or TRIANGLE2. We could do all of our operations using only a variable of one of these types, provided that we fill it up with the appropriate values for the sides. That is, a TRIANGLE0 with sides 1, 2 and 3 has the same area as a TRIANGLE1 with the same sides. With this idea in mind, we can do the following:

We can just create a variable of type TRIANGLE0 and fill it up with the contents of our desired register. We can then reference the SIDE fields directly, without the need for all of that messy reflection code. The price we pay for this convenience, however is in essence a copy operation. Whether this is slower than using the reflection interface I can't say (though I suspect it isn't), but it is in any case cleaner.

Not only that, but we can now handle any number of triangles without increasing the number of lines in the code. The only modification we need to make is to set the num_triangles field to the appropriate value.

I'd propose one final refactoring step. Why do we have to define the methods that compute the area and the perimeter inside the reference model? A triangle register contains all of the information required to compute these values. Seeing as how we'll just be using the TRIANGLE0 subtype in our code, we can extend that to contain a get_area() method:

Of course, we can do the same for the perimeter aspect (not shown here). Let's take a moment to see what we've achieved. We've managed to program our computations in a generic way, by relying on methods that take the index of a register as a parameter. This saves us a lot of typing because we don't have to define a method that accesses each field. We've also nicely encapsulated our methods: all methods that refer to a single triangle (get_area() and get_perimeter()) are defined in the triangle register struct, while the methods that refer to all triangles are encapsulated in the reference model struct.

Further above, I've mentioned the bonus requirement that we want our resulting code to look as similar as possible to the case where the register definitions aren't flattened. Let's see how our reference model would look in the ideal case.

Notice that we don't need the get_triangle_regs() method anymore, as we already have our triangles organized in a list. If we were to implement the last proposal, once our register definitions would be fixed, migrating to the new structure would only require some minor search and replace operations. This goes to show that starting off on the wrong foot doesn't mean we're completely out of the dance. With some extra work, we can get very close to the ideal solution, but we have to be willing to compromise a bit on simulation speed. Still, it's better than compromising on maintainability and getting stuck in an endless loop of bad coding style.

I hope you found this post useful. I've posted the code to SourceForge for reference. Stay tuned for more!

Saturday, November 1, 2014

I've been working a lot with vr_ad lately. It has a lot of nice features for modeling registers, but unfortunately not all of them are documented. I'm going to do a longer series of posts based on my recent experiences.

Let's start out small. We've all had the case where accesses to one register of the design affect the values of other registers. This might be best illustrated with a concrete example. Let's say we have a status register defined as follows:

The status flags are updated by the device. Using the control register's SETVALID and CLRVALID fields, we can affect the value of the VALID status flag. Whenever our VALID flag is set, our DUT can start crunching data. The operation is triggered by writing the START field of the control register and terminates within 5 clock cycles. The DONE status flag is set by the hardware once the operation completes and can be cleared by writing to the CLRDONE control field.

In the past I implemented this by giving the affecting register pointers to the affected registers and implementing the logic inside the post_access(...) method. While this works, vr_ad provides a neater way of doing it.

What we first need to do is to mark the status register as an observer of the control register:

Now, whenever the control register is accessed, a method called indirect_access(...) is called inside the status register. This method receives the direction of the access, together with the observed vr_ad object (in our case it's a register, but it could just as well be a register file, a memory, etc.). Using this information we can model the evolution of the status flags.

Let's start with modeling the VALID flag. As stated above, writing a '1' to SETVALID will set the flag, while writing a '1' to CLRVALID will clear it. Simultaneously writing '1's to both fields doesn't make sense, so what we'll do in that case is leave the flag unchanged. Let's distil this behavior into a method:

Let's give it a test drive to make sure that everything works. We'll emulate a monitor updating the register model by calling update(...) for writes and compare_and_update(...) for reads. To make things easier, let's wrap these calls into two handy methods to access the registers:

The data argument we pass to these methods represents the data seen on the bus. When reading the status register, if the data we pass to compare_and_update(...) doesn't match the model's value, an error message will appear and we'll know we've made a mistake.

As before, let's test this to make sure it works. First let's set VALID and issue a START command. With an immediate read from the status register we'll only expect to see the VALID flag set. After 5 clock cycle we'll expect to see both VALID and DONE set:

And there we have it: a nice, clean way of modeling register interdependencies. This pattern can be extended to however many other registers might depend on the CONTROL register. Best of all, it encapsulates the modeling logic inside the register that is being affected, as opposed to inside the affecting register. This means that we can easily add and remove observer registers as we please, as they are all independent of each other.

You can find the complete code on SourceForge if you want to try it out.

Have fun with your register modeling!

P.S.

Don't forget to subscribe if you don't want to miss out on any future vr_ad related posts. You can also be notified about new posts via email by using the "Subscribe by Email" box.

About

I am a Verification Engineer at Infineon Technologies, where I get the chance to work with both e and SystemVerilog.
I started the Verification Gentleman blog to store solutions to small (and big) problems I've faced in my day to day work. I want to share them with the community in the hope that they may be useful to someone else.