VHDL Saving input value into another signal - counter

I have a question for my VHDL code. My entire code has to do the following:
input = Minutes + hours + clk
output = AFTER 1 minute: minuten = minuten + 1 (and if needed hours += 1)
So I built a counter, which counts (well for simulation easyness to 1/10th of a second instead of a minute) clock periods untill one minute is over. Then it raises the minute by 1, and send that to the output. I got one problem however: due to setup violation I can't simulate the synthesis well. I thought this violation was in the line: min_save = minutes. I think that it's impossible to save the input minutes immediately, so I thought: let's build another counter, which counts 1 clock period, and then saves it.
So my 2 questions:
Does this indeed solve the setup violation?
If so: why does the signal setup have no value. It has a value of undefined. What do i do wrong?
If not: why not, and how can I solve this then?
CODE:
architecture behaviour of internclk_u is
begin
process (minuten)
begin -- switching input minutes to binary.
minintern(6) <= minuten(0);
minintern(5) <= minuten(1);
minintern(4) <= minuten(2);
minintern(3) <= minuten(3);
minintern(2) <= minuten(4);
minintern(1) <= minuten(5);
minintern(0) <= minuten(6);
end process;
process (uren)
begin -- switching input hours to binary
uurintern(5) <= uren(0);
uurintern(4) <= uren(1);
uurintern(3) <= uren(2);
uurintern(2) <= uren(3);
uurintern(1) <= uren(4);
uurintern(0) <= uren(5);
end process;
process (clk)
begin
setup <= '0';
if(clk'event and clk = '1') then
if(setup < '1') then
setup <= '1';
else
setup <= setup;
if(min_save = minutes) then
count <= new_count;
else
min_save <= minutes;
count <= (others => '0');
end if;
end if;
end if;
end process;
process (count, minintern)
begin -- count one minute (now its 0.1 second)
if(count < F/10 ) then
new_count <= count + 1;
intern_out <= minintern;
uurintern_out <= uurintern;
else
case minintern is -- Calculate new value for output
when "0001001" => intern_out <= "0010000";
uurintern_out <= uurintern;
when "0011001" => intern_out <= "0100000";
uurintern_out <= uurintern;
when "0101001" => intern_out <= "0110000";
uurintern_out <= uurintern;
when "0111001" => intern_out <= "1000000";
uurintern_out <= uurintern;
when "1001001" => intern_out <= "1010000";
uurintern_out <= uurintern;
when "1011001" => intern_out <= "0000000";
case uurintern is
when "001001" => uurintern_out <= "010000";
when "011001" => uurintern_out <= "100000";
when others => uurintern_out <= uurintern + 1;
end case;
when others => intern_out <= minintern + 1;
uurintern_out <= uurintern;
end case;
new_count <= count;
setup <= '0';
end if;
end process;
process (intern_out) -- Reversing signals for next blocks in system
begin
interne_tijd_m(6) <= intern_out(0);
interne_tijd_m(5) <= intern_out(1);
interne_tijd_m(4) <= intern_out(2);
interne_tijd_m(3) <= intern_out(3);
interne_tijd_m(2) <= intern_out(4);
interne_tijd_m(1) <= intern_out(5);
interne_tijd_m(0) <= intern_out(6);
end process;
process (uurintern_out)
begin -- Reversing signals for next blocks in system
interne_tijd_u(5) <= uurintern_out(0);
interne_tijd_u(4) <= uurintern_out(1);
interne_tijd_u(3) <= uurintern_out(2);
interne_tijd_u(2) <= uurintern_out(3);
interne_tijd_u(1) <= uurintern_out(4);
interne_tijd_u(0) <= uurintern_out(5);
end process;
end behaviour;
Remarks:
ignore the 2 case statements; this is because the inputsignals are not binary coded; they are reversed. Besides that: it doesnt count 1,2,4,8,16 etc; it counts: 1,2,4,8,10,20 etc.
EDIT
I put in the whole architecture now.
The error message I received was something like: "Setup time violations occured(?)". So I believe I cant save the input minutes immediately when I receive minutes. Thats why I tried to program in the setup signal. Hope you guys can help. This error gave me a headache and lots of frustration already:(
EDIT 2
Forget about the whole "setup" signal. A student-assistent pointed out to me that the saving of the input doesnt make any sense. So my updated question is: How can I properly save the input signal/value into another signal?

Your code is really a mess, and it's very difficult to debug... :-(
I think that the problem is due to the fact that you have a multiple definition of the signal setup. Moreover the first time is defined inside a clocked process (even if the setup <= '0' is outside the if statement). The second time is defined inside another process that is not clocked. I think that the synthesis tool doesn't know how to proceed exactly. In the simulation of the RTL code you should normally see some glitches on the setup signal. I also think that the type of setup is set_logic. Try to change it to std_ulogic. The simulation tool will not compile your code since this type is unresolved. Fix the code and then go back to std_logic.
Another thing: you wrote if setup < '1' then: this sounds very strange to me (I didn't know that it was accepted). I prefer to write if setup /= '1' then: it is much more easier to read this kind of code...

Related

Input Signal Edge Detection on FPGA

I am trying to interface a Virtex 4 (ML401) FPGA and a TIVA C series board using 4 wire SPI (cs, sclk, miso, mosi). The tiva acts as a master and the FPGA as a slave.
I am able to receive the SPI data from the master and display the data on the LEDS present on the FPGA (Method 2). However,
I need to find the rising and falling transitions of the chip select signal (required for my application for synchronization purposes).
I have tried many methods with FIFO's (that work well in simulation) but just don't work on the FPGA, as shown:
NOTE: spi_cs is the asynchronous SPI chip select signal input to the FPGA from the TIVA board, while other signals (spi_cs_s, spi_cs_ss, spi_cs_h2l, spi_cs_l2h, etc) are created internally on the FPGA.
Method 1)
prc_sync_cs: process(clk)
begin
if (clk'event and clk = '1') then
spi_cs_s <= spi_cs;
end if;
end process prc_sync_cs;
spi_cs_l2h <= not (spi_cs_s) and spi_cs;
spi_cs_h2l <= not (spi_cs) and spi_cs_s;
Method 2)
process (spi_cs)
begin
if (spi_cs = '0' or spi_cs = '1') then
-- update ledss with new MOSI on rising edge of CS
spi_cs_ss <= spi_cs_s;
spi_cs_s <= spi_cs;
--leds <= spi_wdata; --leds display the received data on the FPGA (saved into spi_wdata in another process)
-- THIS WORKS ON THE FGPA BUT the edge detection doesn't. Why?
end if;
end process;
spi_cs_h2l <= '1' when (spi_cs_s = '0' and spi_cs_ss = '1') else '0';
spi_cs_l2h <= '1' when (spi_cs_s = '1' and spi_cs_ss = '0') else '0';
leds <= "000000" & spi_cs_h2l & spi_cs_l2h; -- ALL leds are off always (i,e both transitions are '0' always).
Method 3)
prc_sync_cs: process(clk)
begin
if (clk'event and clk = '1') then
spi_cs_ss <= spi_cs_s;
spi_cs_s <= spi_cs;
end if;
end process prc_sync_cs;
prc_edge_cs: process(clk)
begin
if (clk'event and clk = '1') then
spi_cs_ss_del <= spi_cs_ss;
end if;
end process prc_edge_cs;
spi_cs_h2l <= '1' when (spi_cs_ss_del = '1' and spi_cs_ss = '0') else '0';
spi_cs_l2h <= '1' when (spi_cs_ss_del = '0' and spi_cs_ss = '1') else '0';
ALL the methods work perfectly in simulation but not when downloaded on the FPGA. I wrote a process to monitor the transitions more closely (to monitor metastable values, if any):
led_test: process(spi_cs_h2l, spi_cs_l2h)
begin
if spi_cs_h2l = '1' or spi_cs_l2h = '1' then
leds <= "111100" & spi_cs_h2l & spi_cs_l2h;
elsif spi_cs_h2l = 'X' or spi_cs_h2l = 'U' or spi_cs_h2l = 'Z' or
spi_cs_l2h = 'X' or spi_cs_l2h = 'U' or spi_cs_l2h = 'Z' then
leds <= "00001111";
else
leds <= "10101010";
end if;
end process led_test;
The leds are always "10101010" i.e is the else case where both spi_cs_h2l and spi_cs_l2h are = '0'. What am I missing ??
Any pointers would be very helpful as I am stuck with this issue since many days.
UPDATE
Using method 3 of clock domain crossing (as suggested by Jeff), and by initializing all the leds and signals to zero, the process to light up the leds is changed as follows:
led_test: process(spi_cs_h2l)
begin
if rising_edge(clk) then
if spi_cs_h2l = '1' then
leds <= "11110011";
end if;
end if;
end process led_test;
At least one high to low transition of the chip select pin is expected to light up the leds. The SPI chip select pin is receiving a '1' always and when FPGA is started/reset, the leds light up.
How is this possible? How can this false high to low transition occur ?
Method 1
This does not perform any sort of clock domain crossing on the spi_cs signal, and so is not a reliable circuit.
Method 2
The line if (spi_cs = '0' or spi_cs = '1') then is always true in the synthesised design, I wouldn't expect you to be able to detect an edge using this
Method 3
This does provide clock domain crossing for spi_cs, and in general looks pretty good. The reason you see "10101010" on your LEDs, is because they only show something different to this for one clk period at a time, at the start or end of an SPI transaction. This is probably much faster than you can see with the naked eye on the LEDs.
Additionally, the line elsif spi_cs_h2l = 'X' or spi_cs_h2l = 'U' or spi_cs_h2l = 'Z' or spi_cs_l2h = 'X' or spi_cs_l2h = 'U' or spi_cs_l2h = 'Z' then will not translate into any real hardware in the FPGA, because the real hardware does not have a way to check for 'U', 'Z', etc.
Method 3 update
It sounds like spi_cs is actually active low. You need to make sure that the initial values for your signals like spi_cs_s and spi_cs_ss are all correct. In this case, I think you should initialise them all to '1', as this seems to be the normal state for spi_cs. So your signal declarations would look like signal spi_cs_s : std_logic := '1'. You should be able to see this behaving properly in simulation.

Can we use an "if" before with-select -VHDL

I have a code like the following,
with current_display select
char_output <= hours1 & '1' when "0111",
hours2 & '1' when "1011",
mins1 & '1' when "1101",
mins2 & '1' when "1110",
"00000" when others;
but I would like to add two or more options to selecting the display of 7-segment. Adding only one option I make this one,
with set_alarm_switch & current_display select
char_output <= hours1 & '1' when "00111",
hours2 & '1' when "01011",
mins1 & '1' when "01101",
mins2 & '1' when "01110",
alarm_hours1 & '1' when "10111",
alarm_hours2 & '1' when "11011",
alarm_mins1 & '1' when "11101",
alarm_mins2 & '1' when "11110",
"00000" when others;
I wonder if I make this same coding with some "if" statements because if i were to add these guys another property, then I would have a complicated code.
I think all the current answers miss the question: "[...] make this same coding with some "if" statements [...]."
Your current VHDL describes your desired circuit using a parallel assignment statement (with-select). You can easily convert this into multiple sequential statements inside a parallel process:
assign_output_p : process (
-- VHDL-2008: use "all" instead of each signal name
set_alarm_switch,
current_display,
hours1,
hours2,
mins1,
mins2,
alarm_hours1,
alarm_hours2,
alarm_mins1,
alarm_mins2
)
begin
-- default assignment
char_output <= (others => '0');
if (set_alarm_switch = '1') then
-- set alarm case
case (current_display) is
when "0111" =>
char_output <= alarm_hours1 & '1';
when "1011" =>
char_output <= alarm_hours2 & '1';
when "1101" =>
char_output <= alarm_mins1 & '1';
when "1110" =>
char_output <= alarm_mins2 & '1';
when others =>
-- case for other current_display values
-- including XUZ etc.
null;
end case;
else
-- default case
case (current_display) is
when "0111" =>
char_output <= hours1 & '1';
when "1011" =>
char_output <= hours2 & '1';
when "1101" =>
char_output <= mins1 & '1';
when "1110" =>
char_output <= mins2 & '1';
when others =>
-- case for other current_display values
-- including XUZ etc.
null;
end case;
end if;
end process assign_output_p;
Also, I think you should evaluate if this big multiplexer makes sense in your application. Seven-segment displays are not that timing critical, so I advise to include a register stage at some point.
Many companies choose to have a policy about this:
Register inputs, leave outputs combinational => assumption that the next module will register them.
Combinational inputs, register outputs => assumption that the previous module has registered outputs.
Personally I would treat each 7 segment display as its own signal. You might have good reasons not to do this, but it makes the most sense to me. So your code might look like this:
seg3 <= hours_tens_time when current_display = '1' else hours_tens_alarm;
seg2 <= hours_ones_time when current_display = '1' else hours_ones_alarm;
seg1 <= mins_tens_time when current_display = '1' else mins_tens_alarm;
seg0 <= mins_ones_time when current_display = '1' else mins_ones_alarm;
This way, you can add more signals as you want to on seg3, 2, 1, 0, if you want to add a stopwatch or whatever you can do that, just expand this conditional assignment above. Give your signals more meaningful names too. hours1 hours2 doesn't mean anything.

ONE clock period pulse based on trigger signal

i am making a midi interface. UART works fine, it sends the 8 bit message along with a flag to a control unit. When the flag goes high, the unit will store the message in a register and make a clr_flag high in order to set the flag of UART low again. The problem is that i can not make this clr_flag one period long. I need it to be ONE period long, because this signal also controls a state machine that indicates what kind of message is being stored (note_on -> key_note -> velocity, for example).
My question here is, how can a signal (flag in this case) trigger a pulse just for one clk period? what i have now makes almost a pulse during a clock period, but i does it twice, because the flag has not become 0 yet. ive tried many ways and now i have this:
get_data:process(clk, flag)
begin
if reset = '1' then
midi <= (others => '0');
clr_flag <= '0';
control_flag <= '0';
elsif ((clk'event and clk='1') and flag = '1') then
midi <= data_in;
clr_flag <= '1';
control_flag <= '1';
elsif((clk'event and clk='0') and control_flag = '1') then
control_flag <= '0';
elsif((clk'event and clk='1') and control_flag = '0') then
clr_flag <= '0';
end if;
end process;
the problem with this double pulse or longer than one period pulse(before this, i had something that made clr_flag a two period clk pulse), is that the system will go though two states instead of one per flag.
so in short: when one signal goes high (independent of when it goes low), a pulse during one clock period should be generated.
thanks for your help.
The trick to making a single cycle pulse is realising that having made the pulse, you have to wait as long as the trigger input is high before getting back to the start. Essentially you are building a very simple state machine, but with only 2 states you can use a simple boolean to tell them apart.
Morten is correct about the need to adopt one of the standard patterns for a clocked process; I have chosen a different one that works equally well.
get_data:process(clk, reset)
variable idle : boolean;
begin
if reset = '1' then
idle := true;
elsif rising_edge(clk) then
clr_flag <= '0'; -- default action
if idle then
if flag = '1' then
clr_flag <= '1'; -- overrides default FOR THIS CYCLE ONLY
idle <= false;
end if;
else
if flag = '0' then
idle := true;
end if;
end if;
end if;
end process;
There are several issues to address in order to make the design for a one cycle
pulse using flip flops (registers).
First, the use of flip flops in hardware through VHDL constructions typically
follows a structure like:
process (clk, reset) is
begin
-- Clock
if rising_edge(clk) then
-- ... Flip flops to update at rising edge
end if;
-- Reset
if reset = '1' then
-- Flip flops to update at reset, which need not be all
end if;
end process;
So the get_data process should be updated accordingly, thus:
Sensitivity list should contain only clock (clk) and reset
The nested structure with if on event should be as above
Only rising edge of clk should be used, thus no check on clk = '0'
Making a one cycle pulse on clr_flag when flag goes high can be made with a
synchronous '0' to '1' detector on flag, using a version of flag that is
delayed a single cycle, called flag_ff below, and then checking for (flag =
''1) and (flag_ff = '0').
The resulting code may then look like:
get_data : process (clk, reset) is
begin
-- Clock
if rising_edge(clk) then
flag_ff <= flag; -- One cycle delayed version
clr_flag <= '0'; -- Default value with no clear
if (flag = '1') and (flag_ff = '0') then -- Detected flag going from '0' to '1'
midi <= data_in;
clr_flag <= '1'; -- Override default value making clr_flag asserted signle cycle
end if;
end if;
-- Reset
if reset = '1' then
midi <= (others => '0');
clr_flag <= '0';
-- No need to reset flag_ff, since that is updated during reset anyway
end if;
end process;
Synchronisation and Edge Detection for FSM
The Rise, Edge and Fall outputs will strobe for one cycle when those events are detected. Inputs and outputs are synchronised for use with a Finite State Machine.
entity SynchroniserBit is
generic
(
REG_SIZE: natural := 3 -- Default number of bits in sync register.
);
port
(
clock: in std_logic;
reset: in std_logic;
async_in: in std_logic := '0';
sync_out: out std_logic := '0';
rise_out: out std_logic := '0';
fall_out: out std_logic := '0';
edge_out: out std_logic := '0'
);
end;
architecture V1 of SynchroniserBit is
constant MSB: natural := REG_SIZE - 1;
signal sync_reg: std_logic_vector(MSB downto 0) := (others => '0');
alias sync_in: std_logic is sync_reg(MSB);
signal rise, fall, edge, previous_sync_in: std_logic := '0';
begin
assert(REG_SIZE >= 2) report "REG_SIZE should be >= 2." severity error;
process (clock, reset)
begin
if reset then
sync_reg <= (others => '0');
previous_sync_in <= '0';
rise_out <= '0';
fall_out <= '0';
edge_out <= '0';
sync_out <= '0';
elsif rising_edge(clock) then
sync_reg <= sync_reg(MSB - 1 downto 0) & async_in;
previous_sync_in <= sync_in;
rise_out <= rise;
fall_out <= fall;
edge_out <= edge;
sync_out <= sync_in;
end if;
end process;
rise <= not previous_sync_in and sync_in;
fall <= previous_sync_in and not sync_in;
edge <= previous_sync_in xor sync_in;
end;
Below is a way of creating a signal (flag2) that lasts exactly one clock period from a signal (flag1) that lasts at least one clock period.
I don't program in VHDL~ here is what I usually do for the same propose in Verilog:
always #(posedge clk or negedge rst) begin
if(~rst) flgD <= 1'b0;
else flgD <= flg;
end
assign trg = (flg^flgD)&flgD;
I am new to verilog and this is the sample code, which I used for triggering. Hope this serves your purpose. You can try same logic in VHDL.
module main(clk,busy,rd);
input clk,busy; // busy input condition
output rd; // trigger signal
reg rd,en;
always #(posedge clk)
begin
if(busy == 1)
begin
rd <= 0;
en <= 0;
end
else
begin
if (en == 0 )
begin
rd <= 1;
en <= 1;
end
else
rd <= 0;
end
end
endmodule
The below verilog code shall hold the value for the signals for one clock cycle exactly.
module PulseGen #(
parameter integer BUS_WIDTH = 32
)
(
input [BUS_WIDTH-1:0] i,
input clk,
output [BUS_WIDTH-1:0] o
);
reg [BUS_WIDTH-1:0] id_1 = 0 ;
reg [BUS_WIDTH-1:0] id_2 = 0 ;
always #(posedge clk)begin
id_1 <= i;
id_2 <= id_1;
end
assign o = (id_1 & ~id_2);
The way to achieve this is to create a debounce circuit. If you need a D flip-flop to change from 0 to 1, only for the first clock, just add an AND gate before its input like the image below:
So here you can see a D flip-flop and its debounce circuit.
P.S. Circuit created using this.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--input of minimum 1 clock pulse will give output of wanted length.
--load number 5 to PL input and you will get a 5 clock pulse no matter how long input is.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.std_logic_unsigned.all ;
entity fifth is
port (clk , resetN : in std_logic;
pdata : in integer range 0 to 5; --parallel data in. to choose how many clock the out pulse would be.
din : in std_logic;
dout : out std_logic
) ;
end fifth ;
architecture arc_fifth of fifth is
signal count : integer range 0 to 5;
signal pl : std_logic; --trigger detect output.
signal sample1 : std_logic;
signal sample2 : std_logic;
--trigger sync proccess.
begin
process(clk , resetN)
begin
if resetN = '0' then
sample1<='0';
sample2<='0';
elsif rising_edge(clk) then
sample1<=din;
sample2<=sample1;
end if;
end process;
pl <= sample1 and (not sample2); --trigger detect output. activate the counter.
--counter proccess.
process ( clk , resetN )
begin
if resetN = '0' then
count <= 0 ;
elsif rising_edge(clk) then
if pl='1' then
count<=pdata;
else
if count=0 then
count<=count;
else
count<=count-1;
end if;
end if;
end if ;
end process ;
dout<='1' when count>0 else '0';--output - get the wanted lenght pulse no matter how long is input
end arc_fifth ;

Using switch statement and while loop in conjunction

I am trying to complete the following task:
Create
a
script
that
will
repeatedly
create
a
random
integer
K
in
the
range
of
0
to
20
until
every
case
has
been
entered
at
least
once.
You
have
3
possible
cases.
Case
A
is
entered
when
the
range
of
the
random
integer
K
is
between
or
equal
to
0
and
7.
Case
B
is
entered
when
the
range
of
the
random
integer
K
is
between
or
equal
to
8
and
14.
Case
C
is
entered
when
the
range
of
the
random
integer
K
is
between
or
equal
to
15
and
20.
Rules:
When
a
case
is
entered
you
must
print
to
the
user
“Congratulations
you
entered
Case
(A,
B,
or
C)”.
You
can
only
enter
each
case
once.
If
the
program
attempts
to
enter
the
same
case
more
than
once,
you
must
print.
“Invalid,
that
case
has
already
been
entered”.
The
program
will
end
once
all
the
cases
have
been
entered
and
the
program
will
print
“Good
job,
you
have
entered
all
cases”.
If
the
program
attempts
to
enter
any
already
entered
cases
more
than
3
times
(3
total
times
not
just
for
one
specific
case),
the
program
will
end
and
print
to
the
user
“That
random
generator
wasn’t
random
enough”.
Here is the code I have so fa. It has taken me a couple hours to debug. Am I approaching this the wrong way????Please let me know.
K = round(rand*(20))
flag = 0;
counterA =0;
counterB=0;
counterC=0;
switch K
case {0,1,2,3,4,5,6,7}
fprintf('Congratulations you entered Case A\n')
flag = 1;
counterA = 1
case {8,9,10,11,12,13,14}
fprintf('Congratulations you entered Case B\n')
flag =2;
counterB = 1
case {15,16,17,18,19,20}
fprintf ('Congratulations you entered Case C\n')
flag = 3;
counterC = 1
end
while flag == 1 || flag == 2 || flag ==3
K = round(rand*(20))
if K >=0 && K<=7 && flag==1
disp ('Invalid, that case has already been entered')
counterA = counterA+1
elseif K >=8 && K<=14 && flag ==2
disp ('Invalid, that case has already been entered')
counterB=counterB+1
elseif K >=15 && K<=20 && flag==3
disp ('Invalid, that case has already been entered')
counterC =counterC+1
elseif K >=0 && K<=7 && flag ~=1
counterA =counterA+1
flag == 1;
if counterA==1&&counterB~=2 ||counterA==1&&counterC~=2
fprintf('COngrats guacamole A\n')
end
elseif K >=8 && K<=14 && flag ~=2
counterB=counterB+1
flag == 2;
if counterB ==1&&counterA~=2||counterB==1&&counterC~=2
fprintf('COngratsavacado B\n')
end
elseif K >=15 && K<=20 && flag~=3
counterC=counterC+1
flag == 3;
if counterC==1&&counterA~=2||counterC==1&&counterB~=2
fprintf ('Congratscilantro C\n')
end
end
if counterA==1 && counterB==1 && counterC==1
flag=100;
disp('DONE')
elseif counterA == 3|| counterB==3 || counterC==3
disp ('That random generator wasnt random enough')
flag =99;
elseif counterA==2||counterB==2||counterC==2
disp('Inval')
end
Some words about your code:
Don't use variable names like counterA,counterB,counterC, use a array with 3 elements instead. In this case: You need only a total limit, thus one variable is enough.
rand*20 generates random values between 0 and 20, but using round(rand*20) causes a lower probability for 0 and 20. Use randi if you need integers.
Use "Start indent" to format your code clean, it makes it easier to read.
This is not a full solution, the part with the 3 errors is missing. I think you will get this on your own.
caseNames={'A','B','C'};
caseEntered=[false,false,false];
%while there exist a case which is not entered and limit is not reached, continue
while ~all(caseEntered)
K = randi([0,20]);
switch K
case {0,1,2,3,4,5,6,7}
cs=1;
case {8,9,10,11,12,13,14}
cs=2;
case {15,16,17,18,19,20}
cs=3;
end
if caseEntered(cs)
%case has previously been entered, tdb
else
%case is entered frist time
fprintf('Congratulations you entered Case %s\n',caseNames{cs});
caseEntered(cs)=true;
end
end

Is there a better way to re-write a BCD_counter in VHDL code with less "if-statement"?

I am just beginning to learn VHDL in modelsim, so i apologize in advance if what I'm doing seems really noob.
Basically what i am trying to create is a synthesizable VHDL code for a one-digit up/down BCD counter. The counter will count when "Enable" is '1', or else it stays the same. When input "Init" is initialized, the counter is set to 0 or 9 depending on the value of "Direction" input. (When "Direction" is '1', it is an up counter).
I'm just wondering if there are better tools to use for this to work other than using 100 if and else in a row.
Here is my code, I am writing a test bench for it right now, so I am not yet sure if this will even work. So if you happen to also spot some mistake please point it out for me.
Thanks a lot in advance, and here is my code
entity BCD_counter is
port(clk, direction, init, enable: in bit;
q_out: out integer);
end entity BCD_counter;
architecture behaviour of BCD_counter is
signal q: integer;
begin
process(clk)
begin
if(Clk'event and Clk = '1') then
if(direction = '1') then -- counting up
if(init = '1')then --initialize
q<=0; -- reset to 0
else
if(enable = '1')then -- counting
if (q<9) then
q<=q+1;
else
q<=0;
end if;
else
q<=q;
end if;
end if;
elsif(direction = '0') then --counting down
if(init = '1') then --initialize
q<=9; --reset to 9
else
if(enable = '1') then --counting
if (q>0) then
q<=q-1;
else
q<=9;
end if;
else
q<=q;
end if;
end if;
end if;
end if;
end process;
q_out <= q;
end architecture behaviour;
Slightly different style, but this is how I would write it
architecture behaviour of BCD_counter is
signal next_q : integer;
signal q : integer;
begin
pReg : process
begin -- process pReg
wait until clk'event and clk = '1';
if init = '1' then
q <= 0;
else
q <= next_q;
end if;
end process pReg;
pCount : process (direction, enable, q)
begin -- process pCount
next_q <= q;
if enable = '1' then
if direction = '1' then
next_q <= q + 1;
if q = 9 then
next_q <= 0;
end if;
else
next_q <= q - 1;
if q = 0 then
next_q <= 9;
end if;
end if;
end if;
end process pCount;
q_out <= q;
end architecture behaviour;
Points to note:
The register behaviour is in a separate process from the logic. I find this style is clean. Others have different opinions which normally come down to not liking having next_blah signals.
init is your reset signal, so it overrides everything else, and I put reset signals in the register process.
The first line of the counter process is what I want to happen by default. In this case it says have the next state of q be the current state.
I have the outer if be the check of enable. If we're not enabled we don't do anything, so why check direction first?
Inside each half of the direction condition the code structure is the same. Set-up what I want the normal case to be, and then check for the exception to the rule. For example: if we're going up, I set the next state to be q+1, but if q is 9 I override it with q <= 0.
I'm not using >, < in any comparisons. These are more expensive than =, and = is just fine.
Alternatively you could take the enable into the register process. It's slightly shorter, and possibly clearer. It also makes it obvious that you're expecting to use the enable pin of a register for this function.
pReg : process
begin -- process pReg
wait until clk'event and clk = '1';
if init = '1' then
q <= 0;
else
if enable = '1' then
q <= next_q;
end if;
end if;
end process pReg;
pCount : process (direction, q)
begin -- process pCount
if direction = '1' then
next_q <= q + 1;
if q = 9 then
next_q <= 0;
end if;
else
next_q <= q - 1;
if q = 0 then
next_q <= 9;
end if;
end if;
end process pCount;
The only thing that comes to my mind is that you could ommit the two
else
q<=q;
statements because this is implicitly done if none of the other conditions check out.
I do a lot of this - I created a function called modulo_increment which takes an input integer, and "modulus" integer and returns the wrapped around value.
so you can then do
q <= modulo_increment(q, 10);