5/8/2003 - Braindump on simulator design
Been thinking quite a bit about how I should structure the simulator. First, here's what I want out of it:
I don't need it to be fast. Later on, I can do a functional simulator.
The first topic, validating the microcode, is fairly straight-forward. My first simulator did that well, but had some significant short-comings. It's design reflected a fairly high-level view of the architecture. Signals had two states - high and low, and I didn't take timing into account. I had quite a bit of trouble in the beginning with ordering of the unit handlers to get things to work.
For this simulator, I want it to follow the low-level design as much as possible. One of the problem in doing a simulator to validate a design is that you can often do the design and the simulator from similar abstraction levels. This can easily lead to duplicated errors based on similar misunderstandings of how things actually work. I did the hardware design from the top down - ideally, the simulator will be structed from the bottom up. In an ideal world, I'd simply have the right gate-level simulation tools and life would be golden. However, I don't. I'll build this thing from a signal-centric view, and that will have to do.
So, how do I handle signals? High and lo aren't good enough, but I really don't have the ability to deal with micro-timing details. I keep going around on this, but at the moment I think my signals can have one of five states:
In my first simulator, the order of unit handers was critical to make sure the signals and bus values flowed out to their consumers in the right order. This was bad. My plan now is to have the notion of two kinds of signal update - level and edge. When computing the level signal period, I will go into a loop over the signal generation code and keep repeating it until I have a signal generation pass in which nothing changed. I'll further keep track so I'll know if I have a situation in which I get into an endless loop. I don't think I have such a situation, and it would be good to find out. When computing values during the level period, my logical functions will only generate XX, LO and HI values. Once stability is achieved, I go into an edge pass. A UP or DN value is fed into CLKS and then a single signal generation pass is performed. During this single pass, my logical function can generate any of the 5 clock states. After this pass is complete, I'll do a pass over all of my edge-sensitive components. Finally, I'll then run across the signal lines and covert all DNs to LOs and UPs to HIs.
Lather, rinse and repeat.
I have some concern that I am going to be sensitive to signal computation order during the edge pass. I think I'll just have to deal with this. I've thought a bit about allowing edge values to appear during both level and edge phases (or even not distinguish between them), but it makes my brain hurt.
I envision coding this thing up while looking at the schematic sheets one at a time. For each sheet, there will be one entry for a signal generation pass and another for the latch pass. I'll only UP/DN values to be generated via my logic tables when one of the inputs is UP or DN. By doing this (and having the cleanup pass which converts edge values to HI/LO) the same code can be used for level and edge signal generation.
I'm going to need quite a bit of debugging output, along with verbosity control. Given the need to also be able to know if something changed, that means I need to compare old and new values for all computed signals. To simplify life, I'll funnel all updates into a single update routine. To make this even easier, I'll normalize all signals and busses to 32-bit integers. The update routine will be passed a constant denoting which signal or bus to update, along with the new value. I'll also have an array of signal name strings indexed by the signal constant so I can have as much debugging verbosity as I want. To better control the debug level, I think I'll have three seperate arrays - levels, busses and latches. All of this applies to signals and busses which go out across the backplane. I'm not quite sure how I'll handle busses and lines local to the card. Ideally, I can just ignore them and allow them be subsumed durng the signal generation.