Conversion of sequential time-sensitive algorithm to VHDL

O

Oliver Mattos

Hi,
I have a bit of microprocessor code that looks like this (it's basically bit banging a synchronous serial protocol, with certain timing requirements):

SetPin(A1, HIGH);
delay(100ms)
SetPin(A1, LOW);
delay(10ms)
SetPin(A1, HIGH);
delay(100ms)
for (i=0; i<10; i++) {
SetPin(A1, LOW);
SetPin(A2, (data>>=1)&1 );
delay(10ms)
SetPin(A1, HIGH);
delay(12ms)
}
.... etc.


Basically, it's a sequence of actions happening at variable time intervals.

How would you convert this neatly to VHDL? (I have a clock source of known frequency) I've thought of various methods involving state machines and counters, but they always end up horribly complex.

One method I thought of:

WAIT UNTIL rising_edge(clk);
time <= time+1;
IF time > 0 THEN A1 <= '1'; END IF;
IF time > 100 THEN A1 <= '0'; END IF;
IF time > 210 THEN A1 <= '1'; END IF;
IF time > 310 THEN A1 <= '0'; END IF;
IF time > 310 THEN A2 <= data(0); END IF;
IF time > 410 THEN A1 <= '1'; END IF;
IF time > 510 THEN A1 <= '0'; END IF;
IF time > 510 THEN A2 <= data(1); END IF;
IF time > 610 THEN A1 <= '1'; END IF;
IF time > 710 THEN A1 <= '0'; END IF;
IF time > 710 THEN A2 <= data(2); END IF;
etc....

I'm guessing the above logic will lead to a large slow design and messy code...

Is there a nice and easy way to do this?

Oliver


PS. yes I realize there are bugs in both bits of code, but it gets the example across...
 
R

rickman

Hi,
I have a bit of microprocessor code that looks like this (it's basically bit banging a synchronous serial protocol, with certain timing requirements):

SetPin(A1, HIGH);
delay(100ms)
SetPin(A1, LOW);
delay(10ms)
SetPin(A1, HIGH);
delay(100ms)
for (i=0; i<10; i++) {
SetPin(A1, LOW);
SetPin(A2, (data>>=1)&1 );
delay(10ms)
SetPin(A1, HIGH);
delay(12ms)}

... etc.

Basically, it's a sequence of actions happening at variable time intervals.

How would you convert this neatly to VHDL? (I have a clock source of known frequency) I've thought of various methods involving state machines and counters, but they always end up horribly complex.

One method I thought of:

WAIT UNTIL rising_edge(clk);
time <= time+1;
IF time > 0 THEN A1 <= '1'; END IF;
IF time > 100 THEN A1 <= '0'; END IF;
IF time > 210 THEN A1 <= '1'; END IF;
IF time > 310 THEN A1 <= '0'; END IF;
IF time > 310 THEN A2 <= data(0); END IF;
IF time > 410 THEN A1 <= '1'; END IF;
IF time > 510 THEN A1 <= '0'; END IF;
IF time > 510 THEN A2 <= data(1); END IF;
IF time > 610 THEN A1 <= '1'; END IF;
IF time > 710 THEN A1 <= '0'; END IF;
IF time > 710 THEN A2 <= data(2); END IF;
etc....

I'm guessing the above logic will lead to a large slow design and messy code...

Is there a nice and easy way to do this?

Oliver

PS. yes I realize there are bugs in both bits of code, but it gets the example across...

I don't think that would be so large or slow. You might be able to
improve on it a bit by separating the state machine and counter. Your
state machine would have 11 or 12 states depending. In each state it
waits for the counter to reach zero, then loads a new value into the
counter and moves to the next state. Something along that line should
do the job. The point is that your compares don't have to be from
time zero. Instead they can be relative and you can reuse many of the
values rather than having each point a different value. In the loop
it is the same two wait values over and over. Take advantage of that,
just like you would in software.

Also, I would use a case statement rather than a bunch of ifs. The
logic produced likely won't be different... no I take that back. In a
case statement the tools know that the conditions are mutually
exclusive. In the if structure you are using there is a chain of
priority and the tools aren't always smart enough to figure out that
the conditions are mutually exclusive.

procedure RunTimer (IntervalMS : in natural; Enable : in std_logic;
CurState : inout natural; TimeCntr : inout natural)
is
begin
if (Enable = '1') then
if (TimeCntr = 0) then
TimeCntr <= IntervalMS;
CurState <= CurState + 1;
else
TimeCntr <= TimeCntr - 1;
end if;
end if;
end procedure RunTimer;

case (CurState) is
when 0 =>
A1 <= '1';
RunTiimer(100, CurState, TimeCntr);
when 1 =>
A1 <= '0';
RunTiimer(10, CurState, TimeCntr);
when 2 =>
A1 <= '1';
RunTiimer(100, CurState, TimeCntr);
when 3, 5, 7, 9, 11 =>
A1 <= '0';
A2 <= data AND 1;
RunTiimer(10, CurState, TimeCntr);
when 4, 6, 8, 10, 12 =>
A1 <= '1';
RunTiimer(12, CurState, TimeCntr);
when 13 => -- wait for start trigger
if (Start = '1') then CurState <= 0;
end case;


Something like this. You will need a counter to count down to
milliseconds to use as an enable for the TimeCntr. And of course, I'm
sure the logic doesn't match exactly what you need.

Rick
 
R

rickman

Oh yeah, don't forget to constrain your integer signals so that they
aren't implemented as 32 bits.

Rick
 
B

backhus

Hi,
I have a bit of microprocessor code that looks like this  (it's basically bit banging a synchronous serial protocol, with certain timing requirements):

SetPin(A1, HIGH);
delay(100ms)
SetPin(A1, LOW);
delay(10ms)
SetPin(A1, HIGH);
delay(100ms)
for (i=0; i<10; i++) {
  SetPin(A1, LOW);
  SetPin(A2, (data>>=1)&1 );
  delay(10ms)
  SetPin(A1, HIGH);
  delay(12ms)}

... etc.

Basically, it's a sequence of actions happening at variable time intervals.

How would you convert this neatly to VHDL? (I have a clock source of known frequency)  I've thought of various methods involving state machines and counters, but they always end up horribly complex.

One method I thought of:

WAIT UNTIL rising_edge(clk);
time <= time+1;
IF time > 0 THEN A1 <= '1'; END IF;
IF time > 100 THEN A1 <= '0'; END IF;
IF time > 210 THEN A1 <= '1'; END IF;
IF time > 310 THEN A1 <= '0'; END IF;
IF time > 310 THEN A2 <= data(0); END IF;
IF time > 410 THEN A1 <= '1'; END IF;
IF time > 510 THEN A1 <= '0'; END IF;
IF time > 510 THEN A2 <= data(1); END IF;
IF time > 610 THEN A1 <= '1'; END IF;
IF time > 710 THEN A1 <= '0'; END IF;
IF time > 710 THEN A2 <= data(2); END IF;
etc....

I'm guessing the above logic will lead to a large slow design and messy code...

Is there a nice and easy way to do this?

Oliver

PS. yes I realize there are bugs in both bits of code, but it gets the example across...

Hi,
why invent a state machine, whan you could use a premade one that is
programmable. :)
eg. KCPSM (also known as Picoblaze)

Have a nice synthesis
Eilert
 
D

Dal

Does this need to be synthesisable? If not, why don't you write a
procedure for SetPinA1() and delay()?

procedure delay(t : in time) is
begin
wait for (t);
end procedure;

procedure SetPinA1(val : std_logic) is
begin
wait rising_edge(clk);
A1 <= val;
end procedure;

Then call from inside a process.

Darrin
 
A

Alessandro Basili

I don't think that would be so large or slow. You might be able to
improve on it a bit by separating the state machine and counter. Your
state machine would have 11 or 12 states depending. In each state it
waits for the counter to reach zero, then loads a new value into the
counter and moves to the next state. Something along that line should
do the job. The point is that your compares don't have to be from
time zero. Instead they can be relative and you can reuse many of the
values rather than having each point a different value. In the loop
it is the same two wait values over and over. Take advantage of that,
just like you would in software.

Also, I would use a case statement rather than a bunch of ifs. The
logic produced likely won't be different... no I take that back. In a
case statement the tools know that the conditions are mutually
exclusive. In the if structure you are using there is a chain of
priority and the tools aren't always smart enough to figure out that
the conditions are mutually exclusive.

procedure RunTimer (IntervalMS : in natural; Enable : in std_logic;
CurState : inout natural; TimeCntr : inout natural)
is
begin
if (Enable = '1') then
if (TimeCntr = 0) then
TimeCntr<= IntervalMS;
CurState<= CurState + 1;
else
TimeCntr<= TimeCntr - 1;
end if;
end if;
end procedure RunTimer;

case (CurState) is
when 0 =>
A1<= '1';
RunTiimer(100, CurState, TimeCntr);
when 1 =>
A1<= '0';
RunTiimer(10, CurState, TimeCntr);
when 2 =>
A1<= '1';
RunTiimer(100, CurState, TimeCntr);
when 3, 5, 7, 9, 11 =>
A1<= '0';
A2<= data AND 1;
RunTiimer(10, CurState, TimeCntr);
when 4, 6, 8, 10, 12 =>
A1<= '1';
RunTiimer(12, CurState, TimeCntr);
when 13 => -- wait for start trigger
if (Start = '1') then CurState<= 0;
end case;


Something like this. You will need a counter to count down to
milliseconds to use as an enable for the TimeCntr. And of course, I'm
sure the logic doesn't match exactly what you need.

I suggest you simulate what you wrote, at least to find out that
RunTiimer is defined as RunTimer and then that there is the Enable
signal defined as "in std_logic" which you forgot when calling the
procedure.
I urge the need to draw your attention on the name of the procedure as
well, I believe that RunTimer is misleading since it is neither a Timer
neither is running (what does "run" means in this context?).
On top of it why the counter should be a down counter?

Looking at the structure of his sequence I don't believe the OP needs
more than an FSM and a counter and it looks to me it will need only 4
states:

idle (wait for the conditions to start the sequence)
set1 (used to set 100ms signal)
wait (used to wait 10ms)
set2 (used to set 12ms signals)

The FSM will need some additional logic to distinguish the two phases:

init_done
loop_done

and arcs may follow this logic:

idle -> set1 (start/reset/begin... as you like)
set1 -> wait when timer = 100ms
wait -> set1 when (timer = 10ms) and init_done = 0;
wait -> set2 when (timer = 10ms) and init_done = 1;
wait -> idle when (timer = 10ms) and loop_done = 1;
set2 -> wait when timer = 12ms

In case one day you will find that instead of a loop of 10 you will need
a loop of 200 you will simply need to change the logic for loop_done,
without the need to add 190 states to the FSM, same applies for the init
part.

The code the OP posted in my opinion will not work since the priority
encoder will almost always be set to the first case (time > 0), since it
will always be true except when time = 0 (during rollover).

Al

p.s.: I intentionally avoided to include vhdl since I believe that
different people have different styles.
 
R

rickman

I suggest you simulate what you wrote, at least to find out that
RunTiimer is defined as RunTimer and then that there is the Enable
signal defined as "in std_logic" which you forgot when calling the
procedure.

Thank you for your suggestion. If you would like me to run
simulations for you, please contact me directly and I will be happy to
provide you with my consulting rates and a contract.
I urge the need to draw your attention on the name of the procedure as
well, I believe that RunTimer is misleading since it is neither a Timer
neither is running (what does "run" means in this context?).
On top of it why the counter should be a down counter?

I don't know why you think RunTimer does not run a timer.

if (TimeCntr = 0) then
TimeCntr <= IntervalMS;
else
TimeCntr <= TimeCntr - 1;
end if;

This does not look like a timer to you?

Looking at the structure of his sequence I don't believe the OP needs
more than an FSM and a counter and it looks to me it will need only 4
states:

Isn't that what my code provides?

idle (wait for the conditions to start the sequence)
set1 (used to set 100ms signal)
wait (used to wait 10ms)
set2 (used to set 12ms signals)

The FSM will need some additional logic to distinguish the two phases:

init_done
loop_done

and arcs may follow this logic:

idle -> set1 (start/reset/begin... as you like)
set1 -> wait when timer = 100ms
wait -> set1 when (timer = 10ms) and init_done = 0;
wait -> set2 when (timer = 10ms) and init_done = 1;
wait -> idle when (timer = 10ms) and loop_done = 1;
set2 -> wait when timer = 12ms

In case one day you will find that instead of a loop of 10 you will need
a loop of 200 you will simply need to change the logic for loop_done,
without the need to add 190 states to the FSM, same applies for the init
part.

The code the OP posted in my opinion will not work since the priority
encoder will almost always be set to the first case (time > 0), since it
will always be true except when time = 0 (during rollover).

Al

p.s.: I intentionally avoided to include vhdl since I believe that
different people have different styles.

Ok, that looks great. When you are ready to provide some code let us
know.

Rick
 
A

Alessandro Basili

I don't know why you think RunTimer does not run a timer.

if (TimeCntr = 0) then
TimeCntr<= IntervalMS;
else
TimeCntr<= TimeCntr - 1;
end if;

This does not look like a timer to you?

If it is "only" a timer why don't you call the procedure "timer"?
What happened to CurState? Isn't the procedure taking care also about
the fsm? Why the name of the procedure does not reflect what it is doing?
I would assume that if you have to implement a fifo you will call the
module fifo.vhd as well as the main entity.
Isn't that what my code provides?

Your state machine looks like a counter to me and I believe that fsm are
different from counters.
Ok, that looks great. When you are ready to provide some code let us
know.

I believe I gave enough info to the OP to write the vhdl on his own,
after all I think we are here to exchange ideas and problems, not pieces
of (untested) code.
 
R

rickman

If it is "only" a timer why don't you call the procedure "timer"?
What happened to CurState? Isn't the procedure taking care also about
the fsm? Why the name of the procedure does not reflect what it is doing?
I would assume that if you have to implement a fifo you will call the
module fifo.vhd as well as the main entity.

I have no idea where you are coming from. If you don't like the name
I used, feel free to change it to suit yourself. I'm not writing the
code for work. I was trying to show someone an alternative approach
to solving his problem which would make this code simpler. If you
don't like it why not post some of your own?

Your state machine looks like a counter to me and I believe that fsm are
different from counters.

If you think a counter is not a FSM, then you need to reread your text
books. The OP's problem stepped through a number of states
sequentially. That makes a counter an appropriate form of FSM to
represent his design. Further, he was trying to turn a sequential
language program into hardware. This style of FSM would make it very
easy for him to make that translation.

I believe I gave enough info to the OP to write the vhdl on his own,
after all I think we are here to exchange ideas and problems, not pieces
of (untested) code.

That was my purpose. I didn't intend that he should take my code,
type it in and it would run. In essence I was providing pseudo-code
to express my ideas.

Do me a favor. Next time I try to help someone, feel free to offer
help as well, but don't criticize others for doing the same. Your
criticism didn't help me, it didn't help the OP and I expect it didn't
help you either.

Rick
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top