One software project I’ve wanted to do for quite a while is to code a logic simulator. Well I’ve finally got round to doing so and made reasonable success within under half a days worth of coding! With 444 lines of C++ and 17 lines of Matlab code, I’ve managed to design a simulator which can simulate logic circuits from a netlist, then view the logic output on a Matlab plot!
Viewing the output of an AND gate driven by two clocks
For ease of coding, I made strong use of vectors – essentially dynamically variable sized arrays of any given type. I started by making a master “Gate” class and defining what functions would be required by each type gate. I eventually decided that the “Gate” class would hold every form of logic component, logic gates, flip flops and clocks (soon to include constants too). This class featured functions such as setXYZ and getXYZ where XYZ were various string parameters used to map the inputs and outputs of gates to eachother. Each gate has a defined output and potentially inputs depending on what type of gate it is – for example, the “Clock” gate (wrong I know…) doesn’t have a defined input.
The gate class also includes a “process” and “tprocess” virtual function declaration. The “process” function is inherited by all combinatorial gates and this function calculates the output that the gate should provide from a given input stimulus. The function “tprocess” is used for predominantly the fake clock gate. This function accepts the parameter “time” which is the global simulation time. This is required for the clock to change its output after n time steps.
The simulator accepts a single input text file which contains some simple information about the simulation that is about to be undertaken. The first token (small collection of character[s]) of the line defines different pieces of information for the simulator:
- ts – Time step. This value defines at what rate the simulator should step through time. This is an integer value and the smallest value is therefore 1 (a value of 0 would stall the simulator for ever – there is no error checking!). The value presented after this token will be the time step value e.g. ts 1 would give a time step of 1
- te – End time. The value of time the simulator should run to. The simulator will count from T = 0 up to T = te in steps of ts. The value presented after this token will be the end time e.g. te 100 would run the simulation from 0 to 100 in steps of ts.
- g – This is the designator for a gate. The tokens after this token define which gate is to be used along with what its inputs and outputs should be. The style of this command depends on the type of gate used.
For NOT gates: g [gate ID] not [Input node name] [Output node name] e.g. g notgate not gate1Out notOut – will produce a NOT gate with the ID “notgate”, the input of which being the node “gate1Out” and the output node being “notOut”.For flip flops: g [gate ID] dflopf/dflopr [Data node name] [Clock node name] [Q Output node name] [not Q output node name] e.g. g dff dflopr dfdat dfclk dfq dfnq – will produce a rising edge triggered D flip flop with the data input being the node “dfdat”, the clock input being the node “dfclk”, the Q output being the node “dfq” and the not Q output being the node “dfnq”.
For every other combinatorial gate: g [gate ID] [gate type] [input a node] [input b node] [output node] e.g. g andgate and inp1 inp2 andout – will produce an AND gate with the two inputs defined by the nodes “inp1” and “inp2”, along with the output being defined as the node “andout”.
- o – The final token. This defines what gate outputs should be printed to the output file. The node identifier presented after this token will be the output to print e.g. taking the and gate example above, the line: o andout – will print the output of this AND gate to the simulation output file.
As a quick example, the netlist that produced the graph above looks like so:
g ck1 clk 100 ck1
g ck2 clk 200 ck2
g g1 and ck1 ck2 op
Hopefully it should be reasonably easy to see that ck1 is the output of clock 1 and ck2 is the output of clock 2. Clock 1 has a time period of 100 counts and clock 2 has a time period of 200 counts. As can be seen, the output of clock 1 is connected to input A of the AND gate and the output of clock 2 is connected to input B of the AND gate. The output of the AND gate (op) is printed to the simulation output file, along side clock 1 and clock 2.
For ease of data parsing, a gigantic 2d array is defined with dimensions nOutputs x int(ceil(timeEnd/timeStep)). Each output is stored in this array with every time step. Once the simulation has completed (time == timeEnd), this array is printed to separate lines in a seperate output text file. E.g. for 3 outputs, output 1 will be printed to line 1, output 2 to line 2 and finally output 3 to line 3. While this will drastically slow down the simulation and increase memory usage, it saved decoding interlaced samples in Matlab, allowing for a very small plotting program requirement. Each time sample is seperated from the next by a space to allow for easy delimiter parsing.
To achieve this, lines are read from the simulation output file and parsed into individual numbers. These numbers are stored in an array. Once the entire line has been parsed, this line is plotted to a graph (with hold on enabled) and the array is cleared. The same process happens again for the next line except all these values (which are merely 0’s and 1’s) have a constant offset added allowing the y position of the values to be moved. This is continued until the end of file is reached. This easy method offloads all of the complexity to the simulator though allows for a really small Matlab script. Obviously this renders the Y axis useless though the X axis will always display the sample number (not directly related to time…). Changing “hold on” to “hold all” allows for each signal to have a different colour too.
The simulator can also successfully simulate gates that are driven by their own inputs. This is important for example in asynchronous D flip flop based divider circuits.
Rising edge D flip flop based divider – with Matlab “hold all” command for nice colours!
Netlist for D flip flop based divider:
g ck1 clk 100 ck1
g df1 dflopr nop ck1 op nop
g df2 dflopr nop2 op op2 nop2
It can be seen above however that propagation delay isn’t taken into account though for proof of concept this simulator does seem to work! As of yet, there is minimal error checking so if you write your netlist wrong, it may or may not throw up an error depending on the netlist error!
A final example is a full adder. A full adder consists of 5 logic gates, 2 XOR, 2 AND and 1 OR. This circuit allows binary addition to be modelled using my simulator.
g ck1 clk 100 ck1
g ck2 clk 200 ck2
g ck3 clk 400 ck3
g g1 xor ck1 ck2 g1o
g g2 xor g1o ck3 sum
g g3 and g1o ck3 a1o
g g4 and ck1 ck2 a2o
g g5 or a1o a2o carry
A full adder modelled in my simulator!
This is actually a reasonably interesting project so this should make a post appearance again in the near future!