Jump to content
  • 0

Something simple is apparently impossible. Incr/decr number.


Tickstart

Question

Hi, I'm just beginning with FPGA's, I bought a Basys 3 this past tuesday.

I'm trying to increment a 4-bit value with the on-board Up-button and decrease it with the Down-button. What is a straight forward way of doing this?? I've tried to get this to work for the last two days. The buttons are debounced, so that's not an issue.

I've tried keeping the value in a signal vector and depending on which button is pressed, add a one or add a two's complement -1. The adding is done with a 4 bit adder I built, which works.

 

I'm sort of new to VHDL and digital design so any useful tips or hints or general advice are very much appreciated.

Instead of getting 1, 2, 3, 4, 5, 6 etc when pressing the Up button, I get a very weird pattern.. See attached photo (the arrows were meant to show that the pattern repeated itself after a number of presses).

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--use IEEE.STD_LOGIC_UNSIGNED.ALL;
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity btn_map is
    Port ( btnD, btnU : in STD_LOGIC;							--btnU/D stands for up/down
           key : out STD_LOGIC_VECTOR (3 downto 0));
end btn_map;

architecture Behavioral of btn_map is

component trig_4b_Adder is
    Port ( clk : in STD_LOGIC;			--I modified the adder with some D-flops to not create a combinatorial loop (hence the clk)
           c, y : in STD_LOGIC_VECTOR (3 downto 0);				--c/y are the two 4-bit inputs to the adder
           s : out STD_LOGIC_VECTOR (3 downto 0));				--s = result
end component;

signal val, add_sub, new_key : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
signal trigger : STD_LOGIC := '0';		-- clock for the adder, "keypressed"

begin

    Adder: trig_4b_Adder port map(clk => trigger, c => val, y => add_sub, s => new_key); -- add_sub is either the +1, -1 or 0
    
    process(btnD, btnU)
        variable minus : STD_LOGIC_VECTOR (3 downto 0) := "1111";
        variable plus : STD_LOGIC_VECTOR (3 downto 0) := "0001";
        variable zero : STD_LOGIC_VECTOR (3 downto 0) := "0000";
        begin
            if btnD = '1' then
                add_sub <= minus;
            elsif btnU = '1' then
                add_sub <= plus;
            else
                add_sub <= zero; -- (sub zero lol)
            end if;
        end process;
                               
    trigger <= btnU or btnD;		-- start the adder
    val <= new_key;	-- I want to save the result from the adder till next clock cycle	-- these two lines of code feel bad for some reason,
    key <= new_key; -- key is the output from this module					-- like my design is inherently flawed somehow..
    
end Behavioral;

 

IMG_20170422_040209832.jpg

Link to comment
Share on other sites

Recommended Posts

Dear @Tickstart

I am afraid you are getting confused with multiple points of view. Let me add one more.

FPGAs are used as event driven machines. They produce output based on combination of inputs and these outputs change when there is change on input(s). It can be completely asynchronous machine if you don't need time stimulus. Then there is no need for clock. If your design have regular repetitive event then a clock is your help. Clocks a needed also for generating of signals, communication, and implementing delays.  In VHDL many processes have clock in a list of stimuli but it depends on its purpose. It is necessary for synchronization only.

Events in many control systems are random and the beauty of FPGA that they can respond almost immediately as oppose to clocked processor based controller. The latest industrial PLCs are constructed on FPGAs to achieve fast and deterministic response.

Good luck!

P.S. It should be mentioned that VHDL has operators for simulation only. They are not synthesizable, for example "wait". Clock driven counter is used instead for the implementation.

Link to comment
Share on other sites

Some pointers to absolute beginners in FPGA development.

1. FPGA development using VHDL or Verilog isn't the same as software development.  Using an HDL to express logic in an FPGA is Computer Aided Digital Design. The place to start is understanding the basic concepts of digital design. Take a course in digital design. Read a book about digital design. Using an HDL for logic synthesis is more complicated than using a computer language like C... there are a lot more things that have to be taken into consideration.

2. Once you've grasped the concepts of digital design you're ready to learn Verilog or VHDL in the context of logic synthesis. In the beginning you don't have to understand how a synthesis tool interprets your HDL code and ends up with a configuration file that programs an FPGA. You DO have to understand all of the elements of the HDL that's in your code in the context of digital logic.

3. Once you've grasped the basics of digital design and using an HDL to do digital design you are ready to understand the code for an entity or module written by someone who does. Then you're ready to understand the HDL in a testbench meant to verify that code. The you can learn how to use a simulator to verify the HDL.

4. Once you gotten to this point you are ready to write code for your first module. The very next step is to write a testbench for that module, simulate it and see what's going on. Verification is part of the HDL design process. Trying to do one without the other is just silly.

5. I you ask for help and someone gives it to you then you are obliged to at least try and follow their instructions. If their instructions aren't helping then you should find help from someone else. Simply ignoring instructions is an insult to you and anyone trying to help you. It is arrogant to think that you can use a language that you know that you don't understand and continue to try and get useful results by writing code. The verification step is helpful in finding errors in syntax, timing and design concept. It isn't a cure for trying to use a language that you don't understand.

I post this more explicit rehash of what I've already said in this thread because I suspect that @Tickstart isn't a real person but a computer simulation... perhaps Yahoo trying to get into IBM's Watson space. A lot of things would start making sense to me if this is true.

 

Link to comment
Share on other sites

So, based on what's been written so far. let me pose some questions?

Do you think that I'd use variables to solve your problem? Do you think that I'd use a clocked process?

One thing that hasn't been nailed down is these mysterious debounced button signals. It's hard to analyse and account for things that are unknown.

 

Link to comment
Share on other sites

@Tickstart,

You said debouncing isn't an issue, yet I must be missing your debouncing code above.  Depending on how you are handling your debouncing, the following may or may not apply:

My guess is that you are struggling with a metastability issue on those input buttons.  A metastability issue has to deal with when the button transition happens with respect to the clock.  If it happens long enough before a clock, the signal is valid.  If the transition happens too close to the clock, the bit that gets read from the port might be neither a one nor a zero, and might have different logic consequences depending upon what it is being fed into.  Because when the button press happens is completely asynchronous to the clock (go ahead and try to synchronize your finger to a 10ns boundary, I'd like to watch ;) ), sometimes there will be enough time for the bit to settle, sometimes not.  Now take this unsettled bit and try to apply logic to it.  Will it work?  Maybe.  Sometimes.

Try this: register those input buttons on a clock into a register by themselves.  After two clocks registering these into their own registers, at the rising edge of the clock, you'll have a stable value that you can then use as logic within your design.

Dan

Link to comment
Share on other sites

I Hadn't picked up a board in a while (we are trying to get our house ready for sale, so I've been picking up paint brushes!)

So here is a quick design I knocked out to achieve your aim, with all the tricks I think need to be added. I've found my Basys3 and it works for me - it might work for you too.

The big difference is "use IEEE.NUMERIC_STD.ALL;" and, it allowing the use of "unsigned" data type, to which you can add and subtract.

Feel free to ask questions - most of the design decisions are quite arbitrary and you might have better ideas! 

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity up_down is
    Port ( clk      : in  STD_LOGIC;
           btn1_raw : in  STD_LOGIC;
           btn2_raw : in  STD_LOGIC;
           leds     : out STD_LOGIC_VECTOR (3 downto 0));
end up_down;

architecture Behavioral of up_down is
    -- For synchronizing into the clock domain
    signal btn1_meta       : std_logic := '0';
    signal btn2_meta       : std_logic := '0';
    signal btn1            : std_logic := '0';
    signal btn2            : std_logic := '0';
    
    -- Fro debouncing the synchronised signals 
    -- Length defines the 'hold time' for the debouncing - 2^19 cycles
    signal debounce1       : unsigned(19 downto 0) := (others => '0');
    signal debounce2       : unsigned(19 downto 0) := (others => '0');
    signal debounced1      : std_logic := '0';
    signal debounced2      : std_logic := '0';

    -- For detecting the rising edges of the debounced signals
    signal debounced1_last : std_logic := '0';
    signal debounced2_last : std_logic := '0';
    
    signal count           : unsigned(3 downto 0) := (others => '0');
begin
    leds <= std_logic_vector(count);
  --------------------------------------------
  -- Process to synchronise the button signals
  --------------------------------------------
sync_proc: process(clk)
    begin
        if rising_edge(clk) then
            btn1      <= btn1_meta;
            btn2      <= btn2_meta;
            btn1_meta <= btn1_raw;
            btn2_meta <= btn2_raw;
        end if;
    end process;

   ----------------------------------------------
   -- Process to debounce the buttons, to generate
   -- 'debouncedX' that is stable for 2^19 cycles
   -----------------------------------------------
debounce_proc: process(clk)
    begin
        if rising_edge(clk) then
            if btn1 = '0' then
                if debounce1(debounce1'high) = '1' then
                    -- Count down if the button is lifted
                    debounce1 <= debounce1 - 1;
                else 
                    -- snap down to zero once the MSB clears
                    debounce1 <= (others => '0');
                end if;
            else
                if debounce1(debounce1'high) = '0' then
                    -- Count up if the button is pressed
                    debounce1 <= debounce1 + 1;
                else 
                    -- snap down to zero once the MSB is set
                    debounce1 <= (others => '1');
                end if;
            end if;
            debounced1 <= std_logic(debounce1(debounce1'high));
            
            if btn2 = '0' then
                if debounce2(debounce2'high) = '1' then
                    -- Count down if the button is lifted
                    debounce2 <= debounce2 - 1;
                else 
                    -- snap down to zero once the MSB clears
                    debounce2 <= (others => '0');
                end if;
            else
                if debounce2(debounce2'high) = '0' then
                    -- Count up if the button is pressed
                    debounce2 <= debounce2 + 1;
                else 
                    -- snap down to zero once the MSB is set
                    debounce2 <= (others => '1');
                end if;
            end if;
            debounced2 <= std_logic(debounce1(debounce1'high));            
        end if;
    end process;
    

   ----------------------------------------------
   -- A process to detect the rising edges in the  
   -- debounced signals and then update the counter
   -----------------------------------------------
count_proc: process(clk)
    begin
        if rising_edge(clk) then
            if debounced1 = '1' and debounced1_last = '0' and
               debounced2 = '1' and debounced2_last = '0' then
               -- both buttons pressed at the same time.
                NULL;
            elsif debounced1 = '1' and debounced1_last = '0' then
                -- Count up
                count <= count + 1;
            elsif debounced2 = '1' and debounced2_last = '0' then
                -- Count down
                count <= count - 1;
             end if;
             -- Remember button states for next cycle
             debounced1_last <= debounced1;
             debounced2_last <= debounced2; 
        end if;
    end process;
end Behavioral;
Link to comment
Share on other sites

1 hour ago, D@n said:

@Tickstart,

You said debouncing isn't an issue, yet I must be missing your debouncing code above.  Depending on how you are handling your debouncing, the following may or may not apply:

My guess is that you are struggling with a metastability issue on those input buttons.  A metastability issue has to deal with when the button transition happens with respect to the clock.  If it happens long enough before a clock, the signal is valid.  If the transition happens too close to the clock, the bit that gets read from the port might be neither a one nor a zero, and might have different logic consequences depending upon what it is being fed into.  Because when the button press happens is completely asynchronous to the clock (go ahead and try to synchronize your finger to a 10ns boundary, I'd like to watch ;) ), sometimes there will be enough time for the bit to settle, sometimes not.  Now take this unsettled bit and try to apply logic to it.  Will it work?  Maybe.  Sometimes.

Try this: register those input buttons on a clock into a register by themselves.  After two clocks registering these into their own registers, at the rising edge of the clock, you'll have a stable value that you can then use as logic within your design.

Dan

Worse than metastability is just simple timing issues - a signal takes a short period of time to get along the wires in the FPGA - just using indicative numbers, maybe a nanosecond or so.

If the signal for a button is being used in the top left and bottom right of the chip the downstream logic see a skewed value on the wire. If a design is running at 100MHz (10ns cycle) there is a 1 in 10 chance of failure (a metastability error is much less likely 1 in 1 million chance)

The solution is the same though - for any asynchronous signal latch it in a flip-flop before you use it.

Link to comment
Share on other sites

Oh, here's a few comments on the O.P's code.

Most of the problems is that the adder's clock isn't an actual clock signal - it is the two buttons ORed together. To make this sort of design work you want an adder that has a "Clock Enable" ("CE") signal. That way the clock can tick every cycle, and the 'trigger' signal can drive the "Clock Enable" input.

The more subtle issue is that their is something close to a race condition - the pressing of the buttons changes the input to the adder, but the pressing of the button also triggers the adder. Which will happen first? Who knows?

An experiment in bad form could be to have trigger be "trigger <= NOT(btnU or btnD);" - that way the adder would be clocked by the falling edge of the button signal (i.e. on the downpress the input is set to either 'minus' or 'plus', and on the lift the adder is clocked. However, you still have a race condition - will add_sub be reset to 'zero' before or after the adder actually responds to the clock input?

            if btnD = '1' then
                add_sub <= minus;
            elsif btnU = '1' then
                add_sub <= plus;
            else
                add_sub <= zero; -- (sub zero lol)
            end if;

If you change that bit of code to this, as well as the change to trigger to clock on button lift:

            if btnD = '1' then
                add_sub <= minus;
            elsif btnU = '1' then
                add_sub <= plus;
            end if;

It will most likely give warnings about inferred latches, but it might work as expected.

It is still not good synchronous design though, but still might be interesting. 

 

PS. Good choice of board to learn on too :-)

Link to comment
Share on other sites

Hey guys, thanks for your responses!!

5 hours ago, hamster said:

Most of the problems is that the adder's clock isn't an actual clock signal - it is the two buttons ORed together. To make this sort of design work you want an adder that has a "Clock Enable" ("CE") signal. That way the clock can tick every cycle, and the 'trigger' signal can drive the "Clock Enable" input.

 

An adder with a constant clock but with an 'enable' makes perfect sense to me, thank you for that. I knew what I was doing was unorthodox and in bad practice, but I'm just getting started with VHDL and digital design in general so I'm sort of in the dark about what's appropriate and not. Moving from Haskell to VHDL is quite jarring ;)

That's what I will try next, if that doesn't work I'll come back to you. Not sure how I should implement the enable-functionality though, I'll probably screw up again. This is what my newly D-flipflopped full adder looks like:

entity trig_FA is
    Port ( clk, x, y, c_in : in STD_LOGIC;
           s, c_out : out STD_LOGIC);
end trig_FA;

architecture Behavioral of trig_FA is

component D_flipflop
    Port ( clk, d : in STD_LOGIC;
           q : out STD_LOGIC);
end component;

signal d_s, d_c_out : STD_LOGIC;

begin

    dff_s: D_flipflop port map(clk => clk, d => d_s, q => s);
    dff_c: D_flipflop port map(clk => clk, d => d_c_out, q => c_out);
    
    d_c_out <= (x and y) or (c_in and (x xor y));
    d_s <= x xor y xor c_in;

end Behavioral;

I guess the answer is to modify the D-flipflop to accept an enable huh.. Yep, I shall make it so!

7 hours ago, D@n said:

You said debouncing isn't an issue, yet I must be missing your debouncing code above.

Yes, the debouncing is done (hopefully) in another module (one for each button, I'm trying to object orient a bit) whose output is fed into this btn_map code I posted. The debouncing code is below:

(looking at the code now I realize I can do away with the "temp" signal but that's not an error in itself. Also, the only thing really debouncing anything is the fact that the clock fed into the module is very slow.)

entity btn_press is
    Port ( clk, btn : in STD_LOGIC; -- note that I have fed the clk-signal through a clock divider before it gets here
           pressed : out STD_LOGIC); -- not sure about what frequency exactly but definitely enough to debounce
end btn_press;

architecture Behavioral of btn_press is

signal temp : STD_LOGIC := '0';

begin

    process(clk, btn)
    begin
        if rising_edge(clk) then
            temp <= btn;
        end if;
    end process;
    
    pressed <= temp;
    
end Behavioral;

 

Link to comment
Share on other sites

@Tickstart,

I'm missing the counter in your debouncer.  There should be a counter in there.  You mentioned that the clock was the result of a clock divider.  However, clock divider's in logic don't work--especially if you want to look for the positive edge of the clock.  You'll want to  do something instead like checking for not only the positive edge of the real clock (not a logic generated clock), but a logic signal from your logic clock divider (assuming you aren't dividing the clock via a DCM or PLL ...)

Dan

Link to comment
Share on other sites

Last night while responding to another thread I happened onto your post. It was late, I was tired, and, don't take this the wrong way, but your code gave me a headache just looking at it. i started a reply and ended up abandoning it. Though I've been doing HDL for about 20 years I don't consider myself an expert. I humbly offer some suggestions to newbies.

DON'T be cute or "unorthodox" unless you just want to play around using a simulator to see what happens ( and I totally support and encourage doing just that...) . DO keep your code as simple as possible.

Understand that most FPGA synthesis tools extract common structures from your code and implement their own optimized logic unless you turn the feature off. What a synthesis tool and simulation tool interpret your code to mean is NOT always the same so it's very possible to have code work perfectly in a simulation and not in implementation though there are no timing issues. FPGA vendors offer HDL guides for coding styles that work best with their syntheses tools. Read and use these guides. Occasionally, synthesis tools just make mistakes. Unorthodox coding is probably a good way to find bugs in tools, if that's what you're going for. All of this is to say that keeping things simple helps you in more ways than is obvious.

Hamster's comments offer a valuable point. FPGAs are not computers and HDLs are not computer software languages as far as synthesis goes. You need to be thinking about how to do things differently keeping in mind that storage and states are different in digital logic. Basically, things are either concurrent, and in no particular order, or sequential; clocked or combinatorial. All of these require thinking about timing. Combinatorial processes are prone to race conditions and clocked processes are susceptible to metastability. I still sometimes manage to get a simulator to hang by not taking into account the concept of time and signal delay properly. I tend to use either concurrent statement or clocked processes. VHDL Processes have a sensitivity list. The signals in your processes can change state based on a change of state of a signal in the sensitivity list. I rarely write processes that use signal state changes other than a clock edge, except for asynchronous resets. Hamster has offered some fine advice on resets in his wiki site. Your coding style will influence and reflect your thinking about time. I advise developing a code of basic coding styles and expanding them as your endeavours got more complex. While writing terse and arcane C code might imply a greater skill and intelligence ( or job security ) basic boring HDL is a better friend most of the time.

I rarely use variables except when writing testbenches. There are implications for mixing types in your code. Sometimes doing so is required to make your code more readable but you better understand the implications.

 

Link to comment
Share on other sites

In VHDL there are constants and variables. Constants and variables are assigned values using the ":=" symbol. Signals area assigned values using the "<=" symbol. The difference is that ":=" is an instant assignment while the "<=" is an assignment that occurs after some event. This bring us back to the concept of things happening after time and delays. There's nothing wrong with using variables but using them indiscriminately can be a mental trap letting us forget about timing. Also, there's no need to use variables in VHDL code meant to be synthesized. There's enough to think about for my over-taxed little brain without introducing additional complexity with language features that I don't need for the task at hand.

Link to comment
Share on other sites

For everything posted to a forum like this the coding is for the most part "playing around" code. When you have a commercial product that HAS to be reliable things get extremely complicated quickly. Not only do you have to be taking into account internal FPGA signal delays but as the device substrate temperature changes, due to environmental conditions or just heating due to device operation and driving IO these delays change with temperature. On top of this is just plain "features" and behaviour  of synthesis and simulation tools that cause hidden danger. And that doesn't address the bad assumptions of what our coding in our language of choice implies relative to what it actually implies.  Over the course of my career I've read a lot of literature exposing these issues that would cause even a seasoned professional sleepless nights. I tried to find a few of these for illustration purposes from my ancient archives... but didn't find what I'm looking for yet.

Here is an observation that guides my world view:

The less you know about a topic the simpler it is... the more you know about a topic the more complex it is and the larger the realm if what's yet to be learned becomes.

Back to finding some perspective building articles....

Link to comment
Share on other sites

3 hours ago, Notarobot said:

Hi, here is a good explanation and code for the debouncer.

 

Before I rule it out, I'll do some more comparisons with my old debounce hardware and your suggested one, since I actually built it 'n all. Right now though I've changed too much and I'm not sure what's wrong.

2 hours ago, zygot said:

Though I've been doing HDL for about 20 years I don't consider myself an expert. I humbly offer some suggestions to newbies.

 

Gee, thanks. I'm not trying to be unorthodox I'm just doing my best, I don't know how VHDL should be written, that's why I'm here. I know what variables vs signals are, I thought I used them correctly. Please specify what's wrong (even though that can be hard if you're struggling with where to even begin). I'm a simple man so keep it simple :)

Link to comment
Share on other sites

My comment wasn't meant to as a reflection on you but on me. Understand that we're all learning ( or should be ) even those with many years of experience.

One suggestion is to read code from more seasoned knowledgeable people, simulate it and try to understand why they chose to code things the way they did. 

It's my opinion that solving peoples problems for them are is usually the best way to help them along. Believe me, I do understand your frustration. Unfortunately, the second best way to learn is by figuring out your mistakes... the best way would be understanding others mistakes but take requires quite a bit of expertise, or at least experience. I think that so far you've been given some good guidance in that endeavour in this thread.

 

Link to comment
Share on other sites

2 hours ago, Tickstart said:

Let's roll back for a minute, I'd love to hear how you (as in all of you) would go about this problem :) Just need a fresh pair of eyes. The numbers I get are consistent and wrong, but the adder works in other applications.

Hi!

So here is how I would do it.... 

First thoughts about the design - nothing particularly tricky in timing or complexity, so might as well have a single clock domain running at the board's system clock (100Mhz or 50Mhz or whatever). The only thing that is timing sensitive is the button debounce, so a little bit of tweaking might be needed there.

At a glance the following low level components are needed:

- debounce : a button debouncer. Important point - this just generates a single-cycle pulse when a button has been held down long enough
    inputs  - clk 
            - button_raw
    outputs - button_debounced

- up_down_counter : An Up/Down counter with Clock enable. The guts are as follows:
      if rising_edge(clk) then
         if clock_enable = 1 then
            if count_up = '1' and count_down = '1' then
               NULL;
            elsif count_up = '1' then 
               count <= count + 1;
            elsif count_down = '1' then 
               count <= count - 1;
            end if;
         end if;
      end if;
      
   inputs - clock 
          - clock_enable
          - count_up 
          - count_down
   outputs - digit (4 bits)
      
- next_prev_counter : Much like the up_down counter, but outputs
   "0001", "0010", "0100" or "1000" based on 'next' & 'prev' inputs
  Bonus points for it changes to an output of "0000" if no next or prev input is received for 5 seconds

  inputs - clock 
          - next 
          - prev
   outputs - active (4 bits, 'one hot' encoded)
  
- seven_seg_display :  Takes four 4-bit values and displays them on 
   the seven segment display. Maybe add four 'blink' inputs to indicate
   which digit is active (or use the decimal point?)
   
   (This will be the most demanding of the modules)
   inputs  - clock
           - digit0 (4 bits)
           - blink0 
           - digit1 (4 bits)
           - blink1 
           - digit2 (4 bits)
           - blink2 
           - digit3 (4 bits)
           - blink3
   outputs - segments  (7 bits)
           - segment_select (4 bits)

           
- lock_unlock_test : Takes four 4-bit values and asserts the 'unlock' output. A nice idea would be to debounce this - e.g. the correct code has to be present for a second before the unlock signal is asserted.
   
So then it would be 
- Writing each of these five components
- Testing each of these in isolation (either with simulation or on H/W). For newbies H/W is best, just to see how hard debug is, discovering why simulation is so awesome.

With that done, then just assemble the entire system with a top level 'structural' module, with the following component instances:

- 4 x debounce : One for each button (up, down, left, right)

- 1 x next_prev_counter : Logically used to select the digit with focus - connect the next to the debounced right button, the prev to the debounced left button

- 4 x up_down_counter : One for each digit
     connect the count_up to the debounced up button
     connect the count_down to the debounced down button
     connect the clock_enable to the output of the next_prev_counter, so only one digit will change at a time.
  
- 1 x seven_seg_display :  Wire the digits to each of the up_down_counter instances.
   connect the blink input to the output of the next_prev_counter, so you can see which digit has focus.
           
- 1x lock_unlock_test : Connect all the digits to the outputs of the up_down_counters, and connect the unlock output to the secret device of great importance (or just a spare LED!)

And if your low-level testing has been good, it should all just work! (yes, in a perfect world.. :) )

Link to comment
Share on other sites

Learning curve of HDL design is very steep especially for people without background in digital circuits. The fact that every statement outside a process is concurrent makes it very difficult to control/predict behavior. Plus it is difficult to predict how design decisions will affect compliance with the time constraints. Plus many other factors which one can learn only from making these decisions. I tend to believe that the best way to learn is on other people examples. That is one of the major values of this forum.

Some posters on this forum don’t care to explain what the issue they need to help with, but simply want complete solution. I admire D@n, Hamster and Digilent personnel to have patience for this. I am glad that Tickstart made an effort to explain his project. Thank you, zygot for raising important questions.

Have a good weekend.

Link to comment
Share on other sites

3 hours ago, zygot said:

Here is an observation that guides my world view:

The less you know about a topic the simpler it is... the more you know about a topic the more complex it is and the larger the realm if what's yet to be learned becomes.

Back to finding some perspective building articles....

With HDLs I think it is quite important to "do it your way, and do it wrong" as much as possible and see what happens and understand why it is wrong. For example, the OP's debounce code is most likely a free-running n-bit counter, with code along the lines of:

process(clk)
   begin
      if rising_edge(clk) then
        counter <= counter+1;
      end if;
   end process;

process(counter)
   begin
     if rising_edge(count(counter'high)) then
       debounced_button <= input_button;
     end if;
  end process
 

Which is so close to being the correct way to sample a signal, but is flawed for use in an FPGA for quite subtle reasons that are 'magic' to a newbie. Exploring why it is wrong is the fun part of FPGAs for me.

BTW this is what a 'more correct' version looks like, but still is broken for yet more subtle reasons. :-(

process(clk)
   begin
      if rising_edge(clk) then
        if counter = 0 then
          debounced_button <= input_button;
        end if;
        counter <= counter+1;
      end if;
   end process;

So here is a bulletproof version:

process(clk)
   begin
      if rising_edge(clk) then
        if counter = 0 then
          debounced_button <= synced_button;
        end if;
        counter <= counter+1;
        synced_button <= input_button;
      end if;
   end process;

The take-away wisdom being "always synchronise your async inputs" and "try really hard to never have more than one clock"

Link to comment
Share on other sites

Hamster,

No argument with the general concept of learning from failure. But FPGA development is a complex topic and there are so many things to fail at and so many ways to fail. I think that tickstart is trying to tell us that perhaps he isn't skilled enough to learn that way.

So perhaps I should ask tickstart what preparation he has had for this task. Have you had formal coursework? Is this a homework assignment? Trying to grasp the basic concepts and underlying principals is really hard to do just form reading books on one's own. If one's understanding of the basic concepts is flawed then solving even simple problems like a counter that counts up or down based on button pushes will be really hard and figuring out your errors extremely hard. So tickstart have we failed you by not asking the right questions?

Whenever I get stuck for hours or even days ( yes this happens to me even now ) these are the general reasons

1. my eyesight isn't very good so I miss subtle text errors even after re-reading code over and over. This is more of a problem as time goes on and probably not tickstart's

2. I get into a conceptual loop. I had a hound that would run circles around a cat sitting in a the field because once an odor got into his nose his eyes and ears stopped working. He had the eyesight to spot a motionless cat but eyesight was way down the hierarchy of tools he used to hunt. Sometimes we can get caught into logical circular reasoning. My solution is to start over and us a different approach.

3. Sometimes a basic principal that I know just gets forgotten or more likely I thought that I had it covered when I didn't. Again, solving the problem with a new approach helps.

If tickstarts's problem is preparation, which is something that I'm leaning toward at the moment as our hints don't seem to be making the hoped for impact. We might be expecting too much of him by suggesting that our methodology will work for him. I think that most of us has had that Aha moment when we discover that things we though we knew we didn't understand enough, at least on some subject

Tickstart, we assume that you are using a simulator and know how to use one effectively. Are we correct?

In the old days logic was 14 or 16 pin LSI or MSI devices and simulators were graph paper. It was time consuming and laborious but it forced us to consider the minutia of design considerations; especially timing. At the top of the paper was a square wave with arrows on the rising or falling edge representing a clock. Below were representations of signals with rough timing. I'm not reminiscing about the bad old days but today we toss crap into a simulator and let it do all of the thinking all along believing that we're keeping track of everything that's going on when in reality we're just relying on a LOT of assumptions....   I still draw out basic timing and approaches for complex and even not so complex designs.

Last thought. If I have a design with 9 sub-modules then I have 10 testbenches for simulation. Assuming that code at the bottom of the hierarchy is working is always a recipe for disaster as locating problems only gets harder by adding the complexity of higher level code. Even using code that has been previously tested in a different application might not be doing what we remember it does for the current application.

Anyway, tickstart, if you're not completely done with all of our "help" by now and decided to try out your hand as a lawyer, and Lord knows none of us wants another of those, speak up. Help us help you. Except don't ask me to write you good code.... 

 

Link to comment
Share on other sites

Okay I'll explain my project, just for fun. Then maybe you can help me with some general design decisions (: I really appreciate all your help, honestly. Don't go away.

 

Right, so, at the moment I'm trying to implement a combination lock. You enter four digits in the correct sequence to unlock. I made a state transition diagram for the combination lock with the correct sequence being "1337".

It worked when just connecting the up-button to a wrap-around counter but I want to be able to decrease the number as well.

 

The grime on my finger in the video is oil from messing with the bike earlier today, don't be afraid. 

 

IMG_20170422_220528.jpg

IMG_20170422_220502.jpg

Link to comment
Share on other sites

@hamster,

Wow, I like your approach.  I've always been displeased with my own approach (attached, yet in Verilog) because it only mostly works but has a subtle bug (or two).  I like how you solved the problem above: with a timer controlling the transition of the input pin to a holding stage and then to an output stage.  Nice.

On a more philosophical note, I've always thought of FPGA programming as "infuriatingly complex in its simplicity."  If you look at all of the above discussion, everything above has been *amazingly* simple.  I mean, how much simpler can you get than simple clocks, counters, and buttons, right?   Yet the subtle details in this simplicity are enough to drive you up the wall.  Hence, I use the term "infuriatingly complex" to describe this simplicity.

Dan

P.S. Thanks for the complement, @Notarobot, and please let me extend to you a warm welcome to the list of "mentor"s on this site, since I am by no means a moderator.  I love the challenge of trying to find the bug in someone else's code--something that goes beyond the mundane of just pointing someone towards someone else's solution.  That said, if Xilinx ever builds a toolsuite that, from synthesis to bitstream, is as fast as GCC--then you might not find me on this forum much more.  :P

debouncer.v

Link to comment
Share on other sites

@Tickstart,

A combination lock?  That's cool!  I love it!  :D What will it do when it is unlocked?

I put something of a lock on my own board, but in a completely different way.  My "lock" if you wish to call it that was a combination of switches (BCD 1993, 'cause that was a good year ...).  When the switches were set to the combination and a button was then pressed, the VGA output could be 1) turned off, 2) blanked, 3) set to a test pattern, or 4) initiaized to start presenting a slide show.

The reason why I needed a "lock" was because the kids loved playing with the switches so much.  They quickly learned how to switch the LED's into their "knight-rider" mode (just like Kit's LED's, with a proper fade) where adjusting the switches inverted particular LED's, or to set letters in the 7-Segment display.  Gosh, we put all kinds of things on that display: DADD, DAD (blank last letter--required a serial port command since it wasn't valid hex), BAD, BOY, CAT, COOL, DEAD, DEAF, DUDE, JOY, LEAF, NONE, OAF, and more.  One of my favorites was the Gisselquist Technology (GT) logo ... which included "fireworks" along with it (yes, I'm getting imaginative with a 7-seg display, but it was fun).  Indeed, when showing the "slides" on the VGA, the 7-segment display would display the slide #.  It was pretty cool.  The one thing I never figured out how to do with the 7-segment display was to "dim" various components of the display.

As for help building your device, ... I think others have mostly outlined the things you need.  Holler back if you run into trouble.

Oh, and as for @zygot's advice of going to class and school first ... I was a strong computer programmer when I built my first FPGA design, and I never took any FPGA courses in college.  Yes, computer programmers have a *very* difficult struggle learning how to think in parallel rather than sequentially, I've been there, and I've seen it myself.  A very unusual part of my own background was that, for my first two designs I never used a simulator.  Indeed, one of those designs was quite complex--taking about six months to create.  Instead, I taught the built design how to tell me what was working and what wasn't.  Today, though, I've started using simulation regularly and I would recommend using a simulator over the path I took to any new digital designer.

Dan

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...