• 0
Sign in to follow this  
Followers 0
Tickstart

Something simple is apparently impossible. Incr/decr number.

Question

Posted (edited)

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

Edited by Tickstart

Share this post


Link to post
Share on other sites

57 answers to this question

  • 0

@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

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

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;
Edited by hamster
Add code

Share this post


Link to post
Share on other sites
  • 0
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.

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

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 :-)

Edited by hamster

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

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;

 

Edited by Tickstart

Share this post


Link to post
Share on other sites
  • 0

@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

Share this post


Link to post
Share on other sites
  • 0

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.

 

Share this post


Link to post
Share on other sites
  • 0

@zygot,

"I rarely use variables except when writing testbenches" ??  :huh:  Would you care to explain that to all of us who didn't follow what you were trying to say?

Dan

Share this post


Link to post
Share on other sites
  • 0

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.

Share this post


Link to post
Share on other sites
  • 0

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....

Share this post


Link to post
Share on other sites
  • 0
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 :)

Share this post


Link to post
Share on other sites
  • 0

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.

 

Share this post


Link to post
Share on other sites
  • 0

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.

 

Share this post


Link to post
Share on other sites
  • 0

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.

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

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"

Edited by hamster

Share this post


Link to post
Share on other sites
  • 0

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.... 

 

Share this post


Link to post
Share on other sites
  • 0

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

Share this post


Link to post
Share on other sites
  • 0

@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

Share this post


Link to post
Share on other sites
  • 0

@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

Share this post


Link to post
Share on other sites
  • 0

It seems that FPGA became fashionable once more. It motivates but not enough. In my opinion in order to learn HDL one should have real need to use FPGAs. In my case I simply didn't have any other viable technical solution.

It should be noted that complexity can quickly get at an unmanageable level. For this reason I appreciate very much introduction by Xilinx of IP integrator with block design. It creates upper level of abstraction and automate interfacing. I believe that it follows the industry trends along with Mathworks and others.

Share this post


Link to post
Share on other sites
  • 0

@Notarobot,

22 minutes ago, Notarobot said:

It should be noted that complexity can quickly get at an unmanageable level. For this reason I appreciate very much introduction by Xilinx of IP integrator with block design. It creates upper level of abstraction and automate interfacing. I believe that it follows the industry trends along with Mathworks and others.

And I like vim better than emacs (or eclipse, nano, etc.)  [Ref]  What's your point?  I mean, truly, is this really the appropriate thread to discuss and promote the IP integrator?  @Tickstart is just trying to build some very simple logic within his FPGA.  Do you really think he needs the IP integrator to do that?

Granted, many projects can be simplified by integrating multiple different component pieces and parts together.  Granted, the industry likes solutions that keep you from needing to build the wheel four or more times.  Granted that abstraction is a very good thing for accomplishing this.  That said ...

  1. Xilinx's IP integrator is not the solution to all problems.  This problem, in particular, is a good example of one for which the IP integrator is overkill.
  2. Xilinx's IP integrator will get you hooked on Xilinx solutions, and may therefore make it difficult for you to switch FPGA vendors. 
  3. Other non-IP integrator solutions exist, even for Xilinx FPGA's.
  4. If you wanted to buy a chip with all the capabilities already integrated within it, you would not have bought an FPGA.  You would have bought a microcontroller.  A quick search of any microcontroller on Digikey will give you a long list of supported interfaces, many of them beyond what you can get within an FPGA, even though the list of components looks an awful lot like the stuff you can get via the IP integrator.  But you (or whoever) bought your FPGA because a microcontroller wasn't available to do your task.  As a result, you need to get used to building stuff using components that are not available to the IP integrator.  Hence, a good solid understanding of VHDL or Verilog will go a long ways.
  5. FPGA solutions built with Xilinx's IP integrator can be difficult to debug.  When someone else's IP has an error, and you can't tell why, ... it can be very frustrating.  Just read these forums.  As it is, in my own code, I can understand and explain every warning Vivado gives me and why each of those warnings are appropriate--I just can't explain any of the ones from any of the foreign IP I integrate into my own code.
  6. Not all of the IP Xilinx provides is "good" IP.  For example, Xilinx's Memory Interface Generator (MIG) generates SDRAM interfaces with *horrible* latency--perhaps twice as much latency as the chip itself requires.  In other words, and again, there is a time and place for building your own.

So ... can we please leave the IP integrator discussions to threads for which they are relevant?

Thanks,

Dan

Share this post


Link to post
Share on other sites
  • 0

All,

For the record Zygot never mentioned class or school. I've learned more doing projects like Tickster is doing than anything I learned in a class. But then I have decades of professional experience behind me. Working on real hardware and using simulator tools has expanded my knowledge much more than if I took specific college classes would have. But being mentored by extremely talented people as a young engineer did way more for shaping my ability to self-learn than college did.  The thing is that without guidance most of us would just learn bad habits and not be able to develop the self-critical attitude and structure that builds knowledge. I would never try to learn Yoga from a book... I'd just end up hurting myself. There are extraordinary people who seems to be able to become experts without being challenged by someone more knowledgeable ( I've know a few ) but most of us are mere mortals. Without having the foundation built by extremely gifted engineers demanding personal structure and developing good processes I doubt that I would be able to have done this self-learning effectively. None of this is to knock the benefit of having a gifted professor, if you're lucky enough to have had one.

I asked about formal training because knowing about this would help me better assess where my thoughts should go.

So, if you'd bear with me Tickstart;

Did you create testbenches for your design entities? ( the problem with testbenches is that you need to write HDL in order to test HDL and there's a bit of art to writing testbenches in its own right... but if your skill at writing HDL for synthesis is shaky you probably won't do a lot better with testbenches )

What is your preparation for doing this project?  Books, formal training ( not necessarily in a school ), studying other peoples code?

Before setting out on this project did you try smaller less complex experiments to hone your craft?

Tell us a bit about yourself and where you come from FPGA development-wise. Evidently we're not going to make terrific progress by tomorrow so, if we're not in a big hurry and there are no deadlines, let's slow down and corral the discussion to fit your needs. You start out with "Hi, I'm just beginning with FPGA's" so there's no where to go but forward, right?

 

 

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

1 hour ago, zygot said:

Did you create testbenches for your design entities? ( the problem with testbenches is that you need to write HDL in order to test HDL and there's a bit of art to writing testbenches in its own right... but if your skill at writing HDL for synthesis is shaky you probably won't do a lot better with testbenches )

 

Myeahno not particularly good with test benches. Half the time my outputs are undefined so I just disregard them and assume they work. I test them on hardware instead. I can't understand why Xilinx doesn't automate the testbenches. I've used VHDL testbench generators a bit, but yeah I don't know how to write them.

 

1 hour ago, zygot said:

Tell us a bit about yourself and where you come from FPGA development-wise. Evidently we're not going to make terrific progress by tomorrow so, if we're not in a big hurry and there are no deadlines, let's slow down and corral the discussion to fit your needs. You start out with "Hi, I'm just beginning with FPGA's" so there's no where to go but forward, right?

 

Indeed. Well, I'm a bachelor in computer science, or at least soon after I've presented my thesis etc (which involved a Zynq-7000 chip (the Zedboard)). I did not do any HDL design then though, only C programming. Other than that I have had one course in digital design in which we did some vhdl coding, but that was a long time ago and I wasn't very interested either. When it comes to programming I'm obviously used to more... Neat languages. "Regular" imperative languages like C and Java etc but also declarative ones like Haskell and Prolog.

I honestly don't think my code is that bad, you've just seen a small part of it, the only part that doesn't work, so I'm not too happy about your description of me.

I got the 7-segment screen to work for instance, that at least SOUNDS like a harder thing to implement than a simple up-/down counting variable...

 

Anyway, I don't like using other people's code unless I understand it, so if you're posting code please state what's different about it in comparison to something else. I'm the most impatient person in the world so I need very to-the-point, concrete explanations for why I'm failing. I am not stupid, just uninformed. If there's one thing I've at least got some hang of it's doing mealy-/moore machines by hand with 0's and 1's on paper, karnaugh maps and stuff, which works for small things. I don't really like vhdl's functions and processes and procedures, they're confusing and obfuscating.

 

Anyway, I don't even know how this is normally done. If I tie the clock signal to the adder it will go haywire and add four million things per half second. I have to create some sort of "escapement" I guess, for just letting one signal through at a time...

 I thought about having a dedicated up- vs down counter but apparently it's impossible to connect the same "out variable" or signal to those two modules (signal contention I assume).

 

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.

Edited by Tickstart

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0