views:

1036

answers:

8

I have a (somewhat) large truth table / state machine that I need to implement in my code (embedded C). I anticipate the behavior specification of this state machine to change in the future, and so I'd like to keep this easily modifiable in the future.

My truth table has 4 inputs and 4 outputs. I have it all in an Excel spreadsheet, and if I could just paste that into my code with a little formatting, that would be ideal.

I was thinking I would like to access my truth table like so:

u8 newState[] = decisionTable[input1][input2][input3][input4];

And then I could access the output values with:

setOutputPin( LINE_0, newState[0] );
setOutputPin( LINE_1, newState[1] );
setOutputPin( LINE_2, newState[2] );
setOutputPin( LINE_3, newState[3] );

But in order to get that, it looks like I would have to do a fairly confusing table like so:

static u8 decisionTable[][][][][] =
 {{{{ 0, 0, 0, 0 },
    { 0, 0, 0, 0 }},
   {{ 0, 0, 0, 0 },
    { 0, 0, 0, 0 }}},
  {{{ 0, 0, 1, 1 },
    { 0, 1, 1, 1 }},
   {{ 0, 1, 0, 1 },
    { 1, 1, 1, 1 }}}},
 {{{{ 0, 1, 0, 1 },
    { 1, 1, 1, 1 }},
   {{ 0, 1, 0, 1 },
    { 1, 1, 1, 1 }}},
  {{{ 0, 1, 1, 1 },
    { 0, 1, 1, 1 }},
   {{ 0, 1, 0, 1 },
    { 1, 1, 1, 1 }}}};

Those nested brackets can be somewhat confusing -- does anyone have a better idea for how I can keep a pretty looking table in my code?

Thanks!

Edit based on HUAGHAGUAH's answer:

Using an amalgamation of everyone's input (thanks -- I wish I could "accept" 3 or 4 of these answers), I think I'm going to try it as a two dimensional array. I'll index into my array using a small bit-shifting macro:

#define SM_INPUTS( in0, in1, in2, in3 ) ((in0 << 0) | (in1 << 1) | (in2 << 2) | (in3 << 3))

And that will let my truth table array look like this:

static u8 decisionTable[][] = {
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 1, 1 },
{ 0, 1, 1, 1 },
{ 0, 1, 0, 1 },
{ 1, 1, 1, 1 },
{ 0, 1, 0, 1 },
{ 1, 1, 1, 1 },
{ 0, 1, 0, 1 },
{ 1, 1, 1, 1 },
{ 0, 1, 1, 1 },
{ 0, 1, 1, 1 },
{ 0, 1, 0, 1 },
{ 1, 1, 1, 1 }};

And I can then access my truth table like so:

decisionTable[ SM_INPUTS( line1, line2, line3, line4 ) ]

I'll give that a shot and see how it works out. I'll also be replacing the 0's and 1's with more helpful #defines that express what each state means, along with /**/ comments that explain the inputs for each line of outputs. Thanks for the help, everyone!

+3  A: 

Personally, I'd read it from a configuration file. CSV, perhaps, which is easy to export to from Excel. Or you could just copy and paste from Excel into plain text, which gives you space-separated values, which is equally easy to import.

This also means, given that you are working with C, that you won't have to recompile your code each time the decision table changes.

Ben
+1 - This is Dave Thomas's state machine approach: http://www.nofluffjuststuff.com/blog/scott_leberknight/2004/11/state_machines_by_dave_thomas_nfjs_.html
duffymo
+4  A: 

I'd suggest either (preferred approaches first):

  • Use a macro to intialize each "row" - this will hide the braces within the macro call.
  • Use comments to break up the rows.
  • Use an init function to initialize the context explicitly - perhaps use functions to initialize each section. This is similar to the first option above but has a disadvantage that the init function must be invoked before the state machine can be used.
Stephen Doyle
+2  A: 

if your truth-table is all booleans, you could just collapse it to a list of pairs, e.g.

1111,0000
1110,0110
...

for data compression, represent the values as bytes (two nybbles)...

where/how to store it for soft-coding in your particular embedded-system configuration, only you can say ;-)

Steven A. Lowe
@[EvilTeach] thanks for the edit, but i prefer the y-spelling (see http://en.wikipedia.org/wiki/Nybble)
Steven A. Lowe
@S.A.L.: Great suggestions, thanks! This is indeed one of the ideas that I rolled into my solution -- much appreciated.
HanClinto
+1  A: 

If the truth table is really only 4x4x4x4 then I'd use macros. If it's ever going to grow past that, I'd use Ragel. Chances are it will make smaller, faster C code than you will.

Cameron Pope
Neat tool! As gbarry said, I guess my problem is more targeting Truth Tables than State Machines, but this is an excellent tool to keep in one's quiver. Thanks!
HanClinto
+3  A: 

No need for a multidimensional table. With a 4 bit => 4 bit mapping, you can have a single u8[16] array mapping inputs to outputs. State lookups will be much cheaper, and you can extract individual bits with some shift-and-mask ops.

If the algorithm to populate rows is easy to codify, you could #define a macro to populate each row by index number.

HUAGHAGUAH
I like this answer the best. I might still keep it as a two dimensional array (to array-ify my outputs), but I really like the idea of combining the inputs into a single 4-bit index. Because all inputs and outputs are booleans, this will work nicely. Thanks!
HanClinto
+1  A: 

I don't see any reference to the current state in order to get your output state. This means it is not a state machine, but only a truth table. There are four inputs, so there are only 16 possible input combinations. So, a table with 16 positions ought to do it.

gbarry
Good point -- one of the things I didn't mention is that the future value of one of the input columns is one of the output columns, so there's a small state machine within the truth table. But yes, for simplicity, it's probably best to think of this as a pure state machine. Great comments, thanks!
HanClinto
Actually, mostly it's the state machine that has a truth table inside. The states are latches that go from output to input. So I think you mean you will treat it as a pure truth table. Once it's working, of course, the rest is just semantics.
gbarry
+1  A: 

Usually when you have a problem like this, one tries to reduce it to a simple boolean formula. I don't see why that wouldn't be the best approach here. It would be much more compact and more readable, plus it has the potential to be faster (I imagine a handful of ANDs and ORs would execute more quickly than the collection of multiplies/shifts + memory access needed for the lookup table approach). The easiest way to reduce this table to a boolean formula is with a K-Map.

rmeador
@rmeador: The main reason why I didn't want to reduce it to Boolean logic is just because I wanted it to be both easily readable and easily modifiable. Also, I didn't want to make assumptions in optimizing my Boolean logic that would require my logic to be fully re-programmed on every change.
HanClinto
A: 

how many chickens are in a duck?

026