• Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by hamster

  1. A few reasons are... a - The introduction of logic hazards can cause glitches : https://en.wikipedia.org/wiki/Hazard_(logic) b - Routing of clocks is very complex - It is hard to ensure that the same clock edge appears all over the FPGA at almost exactly the same time. Sometimes this is achieved with 'slight of hand' (e.g. using a on-chip PLL to advance phase of the clock, so that by the time it reaches the edge of the chip is in back phase with the original signal). Low-skew paths also exist, but are restricted to small areas of the FPGA, and the clock has to be connected to the correct pin to be placed and routed correctly. c - FPGAs and their tools are designed to behave predictably under the "synchronous digital design" paradigm (something like https://hps.hs-regensburg.de/scm39115/homepage/education/courses/red/2_SynchronousDigitalCircuitDesignRules.pdf). If you work outside the paradigm you will be fighting against the tools and their assumptions. d - There is almost nothing that you are unable to code in an FPGA friendly way, but there are infinitely many ways to write FPGA-hostile code. If you want your FPGA to place nice with you, you have to play nice with it. So you can either add an RC filter to debounce you switch, or you can sample it using a reliable clock.
  2. I know this sounds wrong, but.... A good way to convert 8-bit binary into Decimal Digits is most likely a 256 entry lookup table, giving an two 4-bit results. Yes, it will seem a lot of code, but it will implement very efficient, is fast, and will use very little power. You can also replace a lot of logic, if your conversion would otherwise involve constants (e.g. if you also need to calculate "out_val = k * in_val + c", like you would to convert degrees C into degrees F)
  3. It does look a little odd. It looks as though it will move bits into data_reg every cycle that "spi_clk" is high, TRANSCIEVE: process(clk) begin if rising_edge(clk) if slave_select = '0' then -- slave is selected if spi_clk_tick = '1' and spi_clk_tick_last = '0' then -- seen the rising edge on spi_clk, so grab the data data_reg <= data_reg(6 downto 0) & mosi_sync; end if; end if; -- remember the current spi_clk_tick signal so we can detect if it -- changes from '0' to '1' in the next cycle spi_clk_tick_last <= spi_clk_tick; end if; end process;
  4. A while ago there was a thread about writing a 'combination lock' design for an FPGA board. I finally got around to updating my Wiki with the design: http://hamsterworks.co.nz/mediawiki/index.php/Combination_Lock A short video of it in action is here:
  5. When I get this it is usually the uppercase/lowercase of the top level signals doens't match those used in the .XDC file. This is the only place case matters in a VHDL project!
  6. To make a single-cycle pulse on 'x_rising_edge' when signal 'x' changes from '0' to '1': signal x_last : std_logic := '0'; signal x_rising_edge : std_logic := '0'; ... inside a clocked process.... if x_last = '0' and x = '1' then x_rising_edge <= '1'; else x_rising_edge <= '0'; end if; x_last <= x;
  7. Yep - completely agree. If you were to use this (I'm thinking of using it for generating a changing VGA pixel clock) you should probably use the inverted 'LOCKED' signal as a reset for the critical bits of your design, or have your design organised so any 'badness' will get flushed out quickly (which shouldn't be a problem for a VGA controller - assuming I don't reset the clock midway through a memory transaction).
  8. Oh - ignore the Debug Hub message - it just means that your project does not have a debug code (aka. Embedded Virtual Logic Analyser) in the project. You will need to tinker with the FSM. My first suggestion would be when the user input changes from what is on the screen, jump to the starting state for the FSM so it re-displays everything. something along the lines of this early on in the FSM, to capture what is on the screen: current_bcd <= bcd; and have the rest of the fsm display what is in "current_bcd" Then, in the end state of the FSM: if bcd /= current_bcd then .... do stuff here to restart the FSM, most likely changing lcd_cmd_ptr end if; That way you should have a reasonably responsive display.
  9. I've never used the this display, but on other display the second line start on address C0, (http://www.avrfreaks.net/forum/problem-second-line-2x16-lcd) So try this: Change 14 => "10"&X"20", -- blank To: 14 => "00"&X"C0", -- Move to second line This might also be of use: https://mil.ufl.edu/3744/docs/lcdmanual/commands.html
  10. So if you add a top-level port of "bcd : in std_logic_vector(7 downto 0)" (enough for two digits as a test) then within the module you could do the following: a. replace "constant" with "signal" on LCD_CMDS b. Add a couple of assignments to overwrite the desired values in the array. Add a couple of concurrent assignments: LCD_CMDS(15) <= x"3" & bcd_value(7 downto 4); LCD_CMDS(14) <= x"3" & bcd_value(3 downto 0); That will overwrite the "Di" in "Digilent" with two digits. Once you get this working, you can follow your nose to add the extra digits (e.g. make 'bcd' wider, overwrite extra digits, change the commands being sent to the display so you can access the second line, remove the shift...)
  11. HI! Ignoring the JA and JB ports that are required to talk to the display hardware, what would your module's ideal interface look like? Would it be a register based interface, where you write data to be displayed, (e.g. digit 0 in register 0): addr[4:0], din[7:0], writeEnable or would it be just a minimal interface for each digit you want to show: digit0[3:0], decimal0, blank0, digit1[3:0], decimal1, blank1, ... digitX[3:0], decimalX, blankX Or would it expose the ASCII nature of the display, allowing you to show almost anything? character0[7:0], character1[7:0], ... characterX[7:0] Or would it be a stream interface: din[7:0], data_enable, first_byte_of_packet Have you thought how you would manage the cursor (if it is needed at all)? Once you have that sketched out, the you are in the position of how to implement the innards of the module...
  12. hamster

    MMCM dynamic clocking

    Hi D@n! Have you checked if output of a SERDESE2 can be looped back into the fabric? Or would you need to push it back through a pin. The max jitter that an MMCM block is guaranteed to lock on is 1ns. Not sure if that is +/- 0.5ns RMS or not. If the SERDES output could be looped back to the MMCM then the the MMCM could potentially wipe the jitter (but you might need to reset the MMCM when you change frequency
  13. hamster

    MMCM dynamic clocking

    Hey, something else I just saw when reading the clocking guide was: MMCM Counter Cascading The CLKOUT6 divider (counter) can be cascaded with the CLKOUT4 divider. This provides a capability to have an output divider that is larger than 128. CLKOUT6 feeds the input of the CLKOUT4 divider. There is a static phase offset between the output of the cascaded divider and all other output dividers. And: CLKOUT4_CASCADE : Cascades the output divider (counter) CLKOUT6 into the input of the CLKOUT4 divider for an output clock divider that is greater than 128, effectively providing a total divide value of 16,384. So that can divide a 600 MHz VCO down to 36.6 kHz.
  14. Have a look at https://www.xilinx.com/support/documentation/user_guides/ug472_7Series_Clocking.pdf - you will want to use a MMCM, as the minimum input frequency for a PLL in 19Mhz. If you use the MCM to get 100MHz you will need to multiply by 50, to get at the absolute minimum VCO frequency of 600MHz, then divide that by 6 to get a 100MHz.clock. (see from page 38 of https://www.xilinx.com/support/documentation/data_sheets/ds181_Artix_7_Data_Sheet.pdf for the operating ranges of the MMCM and PLL). You can also 'cheat' and use an IP block, which will do all the calculations for you.
  15. Prompted by another thread here ( https://forum.digilentinc.com/topic/4180-mmcm-dynamic-clocking/ ), I've been experimenting the the Dynamic Reconfiguration Port on the Artix-7 MMCM You can find the code and constraints for the Basys3 here: http://hamsterworks.co.nz/mediawiki/index.php/MMCM_reset It might be of interest to somebody (e.g. to change a VGA clock frequency dynamically).
  16. hamster

    MMCM dynamic clocking

    I feel a bit bad about posting a minor novel here, but here is an example of going from "5 cycles on, 5 off" (i.e. divide by 10) to "10 on, 10 off" (device by 20). The VCO is initially to 800 MHz with CLK0 being VCO divide by 8.... so after config you get 100MHz. Push the button and you get 800/20 = 40MHz, release the button and you get 80MHz. It is all really hairy in practice! EDIT: Through experimentation I just found that you don't need to reset the MMCM if you are not changing the VCO frequency. So the 'rst' signal in the code below isn't needed (and LOCKED will stay asserted). -------------------------------------------------------------------------------------------------------- -- Playing with the MMCM DRP ports. -- see https://www.xilinx.com/support/documentation/application_notes/xapp888_7Series_DynamicRecon.pdf -- for the Dynamic Reconviguration Port addresses -------------------------------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; library UNISIM; use UNISIM.VComponents.all; entity mmcm_reset is Port ( clk_100 : in STD_LOGIC; btn_raw : in STD_LOGIC; led : out STD_LOGIC_VECTOR (15 downto 0)); end mmcm_reset; architecture Behavioral of mmcm_reset is signal btn_meta : std_logic := '0'; signal btn : std_logic := '0'; signal speed_select : std_logic := '0'; signal counter : unsigned(26 downto 0) := (others => '0'); signal debounce : unsigned(15 downto 0) := (others => '0'); signal clk_switched : std_logic := '0'; signal clk_fb : std_logic := '0'; type t_state is (state_idle_fast, state_go_slow_1, state_go_slow_2, state_go_slow_3, state_idle_slow, state_go_fast_1, state_go_fast_2, state_go_fast_3); signal state : t_state := state_idle_fast; ----------------------------------------------------------------------------- --- This is the CLKOUT0 ClkReg1 address - the only register to be played with ----------------------------------------------------------------------------- signal daddr : std_logic_vector(6 downto 0) := "0001000"; signal do : std_logic_vector(15 downto 0) := (others => '0'); signal drdy : std_logic := '0'; signal den : std_logic := '0'; signal di : std_logic_vector(15 downto 0) := (others => '0'); signal dwe : std_logic := '0'; signal rst : std_logic := '0'; begin MMCME2_ADV_inst : MMCME2_ADV generic map ( BANDWIDTH => "OPTIMIZED", -- Jitter programming (OPTIMIZED, HIGH, LOW) CLKFBOUT_MULT_F => 8.0, -- Multiply value for all CLKOUT (2.000-64.000). CLKFBOUT_PHASE => 0.0, -- Phase offset in degrees of CLKFB (-360.000-360.000). -- CLKIN_PERIOD: Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). CLKIN1_PERIOD => 10.0, CLKIN2_PERIOD => 0.0, -- CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for CLKOUT (1-128) CLKOUT1_DIVIDE => 1, CLKOUT2_DIVIDE => 1, CLKOUT3_DIVIDE => 1, CLKOUT4_DIVIDE => 1, CLKOUT5_DIVIDE => 1, CLKOUT6_DIVIDE => 1, CLKOUT0_DIVIDE_F => 8.0, -- Divide amount for CLKOUT0 (1.000-128.000). -- CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for CLKOUT outputs (0.01-0.99). CLKOUT0_DUTY_CYCLE => 0.5, CLKOUT1_DUTY_CYCLE => 0.5, CLKOUT2_DUTY_CYCLE => 0.5, CLKOUT3_DUTY_CYCLE => 0.5, CLKOUT4_DUTY_CYCLE => 0.5, CLKOUT5_DUTY_CYCLE => 0.5, CLKOUT6_DUTY_CYCLE => 0.5, -- CLKOUT0_PHASE - CLKOUT6_PHASE: Phase offset for CLKOUT outputs (-360.000-360.000). CLKOUT0_PHASE => 0.0, CLKOUT1_PHASE => 0.0, CLKOUT2_PHASE => 0.0, CLKOUT3_PHASE => 0.0, CLKOUT4_PHASE => 0.0, CLKOUT5_PHASE => 0.0, CLKOUT6_PHASE => 0.0, CLKOUT4_CASCADE => FALSE, -- Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE) COMPENSATION => "ZHOLD", -- ZHOLD, BUF_IN, EXTERNAL, INTERNAL DIVCLK_DIVIDE => 1, -- Master division value (1-106) -- REF_JITTER: Reference input jitter in UI (0.000-0.999). REF_JITTER1 => 0.0, REF_JITTER2 => 0.0, STARTUP_WAIT => FALSE, -- Delays DONE until MMCM is locked (FALSE, TRUE) -- Spread Spectrum: Spread Spectrum Attributes SS_EN => "FALSE", -- Enables spread spectrum (FALSE, TRUE) SS_MODE => "CENTER_HIGH", -- CENTER_HIGH, CENTER_LOW, DOWN_HIGH, DOWN_LOW SS_MOD_PERIOD => 10000, -- Spread spectrum modulation period (ns) (VALUES) -- USE_FINE_PS: Fine phase shift enable (TRUE/FALSE) CLKFBOUT_USE_FINE_PS => FALSE, CLKOUT0_USE_FINE_PS => FALSE, CLKOUT1_USE_FINE_PS => FALSE, CLKOUT2_USE_FINE_PS => FALSE, CLKOUT3_USE_FINE_PS => FALSE, CLKOUT4_USE_FINE_PS => FALSE, CLKOUT5_USE_FINE_PS => FALSE, CLKOUT6_USE_FINE_PS => FALSE ) port map ( -- Clock Outputs: 1-bit (each) output: User configurable clock outputs CLKOUT0 => clk_switched, CLKOUT0B => open, CLKOUT1 => open, CLKOUT1B => open, CLKOUT2 => open, CLKOUT2B => open, CLKOUT3 => open, CLKOUT3B => open, CLKOUT4 => open, CLKOUT5 => open, CLKOUT6 => open, -- Dynamic Phase Shift Ports: 1-bit (each) output: Ports used for dynamic phase shifting of the outputs PSDONE => open, -- Feedback Clocks: 1-bit (each) output: Clock feedback ports CLKFBOUT => clk_fb, CLKFBOUTB => open, -- Status Ports: 1-bit (each) output: MMCM status ports CLKFBSTOPPED => open, CLKINSTOPPED => open, LOCKED => open, -- Clock Inputs: 1-bit (each) input: Clock inputs CLKIN1 => clk_100, CLKIN2 => '0', -- Control Ports: 1-bit (each) input: MMCM control ports CLKINSEL => '1', PWRDWN => '0', -- 1-bit input: Power-down RST => rst, -- 1-bit input: Reset -- DRP Ports: 16-bit (each) output: Dynamic reconfiguration ports DCLK => clk_100, -- 1-bit input: DRP clock DO => DO, -- 16-bit output: DRP data DRDY => DRDY, -- 1-bit output: DRP ready -- DRP Ports: 7-bit (each) input: Dynamic reconfiguration ports DADDR => DADDR, -- 7-bit input: DRP address DEN => DEN, -- 1-bit input: DRP enable DI => DI, -- 16-bit input: DRP data DWE => DWE, -- 1-bit input: DRP write enable -- Dynamic Phase Shift Ports: 1-bit (each) input: Ports used for dynamic phase shifting of the outputs PSCLK => '0', PSEN => '0', PSINCDEC => '0', -- Feedback Clocks: 1-bit (each) input: Clock feedback ports CLKFBIN => clk_fb ); speed_change_fsm: process(clk_100) begin if rising_edge(clk_100) then di <= (others => '0'); dwe <= '0'; den <= '0'; case state is when state_idle_fast => if speed_select = '1'then state <= state_go_slow_1; -- High 10 Low 10 di <= "0001" & "001010" & "001010"; dwe <= '1'; den <= '1'; end if; when state_go_slow_1 => if drdy = '1' then state <= state_go_slow_2; end if; when state_go_slow_2 => rst <= '1'; state <= state_go_slow_3; when state_go_slow_3 => rst <= '0'; state <= state_idle_slow; when state_idle_slow => di <= (others => '0'); if speed_select = '0' and drdy = '0' then state <= state_go_fast_1; -- High 5 Low 5 di <= "0001" & "000101" & "000101"; dwe <= '1'; den <= '1'; end if; when state_go_fast_1 => if drdy = '1' then state <= state_go_fast_2; end if; when state_go_fast_2 => rst <= '1'; state <= state_go_fast_3; when state_go_fast_3 => rst <= '0'; state <= state_idle_fast; end case; end if; end process; dbounce_proc: process(clk_100) begin if rising_edge(clk_100) then if speed_select = btn then debounce <= (others => '0'); elsif debounce(debounce'high) = '1' then speed_select <= not speed_select; else debounce <= debounce + 1; end if; -- Syncronise the button btn <= btn_meta; btn_meta <= btn_raw; end if; end process; show_speed_proc: process(clk_switched) begin if rising_edge(clk_switched) then counter <= counter + 1; led(7 downto 0) <= std_logic_vector(counter(counter'high downto counter'high-7)); end if; end process; led(15) <= speed_select; end Behavioral;
  17. hamster

    MMCM dynamic clocking

    1. Yes, the MMCM has ports that allow reprogramming - but it is quite involved. See around page 75 of https://www.xilinx.com/support/documentation/user_guides/ug472_7Series_Clocking.pdf Making sure that your timing constraints are correct is also quite tricky. 2. No, you will not get that range out if it - it still has to be in spec for the MMCM (from a few MHz to a few hundred - check the datasheet). Also switching will cause glitches if not done right - to avoid this you need to use a BUFGMUX, (NOTE: for a BUFGMUX to switch, you need to clock edges appearing on both of it's inputs!) I've not done it myself, but I suggest you use use the MMCM to generate a few fixed frequencies (e.g. 33MHz, 10MHz) then have divide by 10 counter hung of them, to give you 3.3MHz, 1.0MHz, 333kHz, 100kHz and so on. Then have two N:1 MUXes use a few to select which frequency clock you want to use. e.g: case a is when "000" => clk_a <= clk_33000kHz; when "001" => clk_a <= clk_10000kHz; Then feed clk_a and clk_b into a BUFGMUX (you may need a BUFG to here to get the signals back on the clocking networks), and switch between them - first change the input used for the inactive clock to the desired frequency (e.g. if clk_a is being used, change the selection for clk_b) then change the select input to the BUFGMUX to switch over onto that clock. Here's an experiment of mine, that might give you ideas: http://hamsterworks.co.nz/mediawiki/index.php/Single_Step
  18. I collected 6GB more data today, and took some photos. I'll make a blog / build-log entry over the next few days. It was using the testpoints on the KiwiSDR cape for a beaglebone - http://kiwisdr.com/
  19. I used the Nexys2's EPP-like parallel interface on my latest (not all that much) FPGA related project - a software GPS receiver. The FPGA was used to capture the raw one-bit samples form a GPS receiver front end at 5,456 MB/s for 200 seconds, without dropping a single bit. I miss the ease of use and the high bandwidth of the old EPP interface... The FPGA code and the C source to read data from it is at https://github.com/hamsternz/Full_Stack_GPS_Receiver/tree/master/misc
  20. If you do want to use your input pin as a clock you can, by routing the signal through a BUFG primative - after you include the something like: -- include the library of primitives library UNISIM; use UNISIM.VComponents.all; ........ i_BUFG: BUFG PORT MAP ( i => raw_pin, o => slow clock); The downside is that the skew between the input clock and the clock signal inside the FPGA isn't the best - but for 8.x MHz or it will have no impact. You might also want to consider using a FIFO, esp if the amount of data is high. Assigning one array to the other requires two flip-flops per bit, however a FIFO can use either Block RAM or distributed RAM and only needs to hold one copy of the data, so will use a lot less resources. The FIFO can also do data width conversion and provide flow control signals too.
  21. @Notarobot's code is much the same as mine in function. But style is a different. I wonder why it isn't just a single process like this: process(clk) begin if(rising_edge(clk)) then if nreset = '0' then if DFF1 /= DFF2 then q_reg <= (others => '0'); -- reset counter elsif q_reg(N-1) = '0' then q_reg <= q_reg + 1; else DB_out <= DFF2; end if; DFF1 <= button_in; DFF2 <= DFF1; else q_reg <= (others => '0'); DFF1 <= '0'; DFF2 <= '0'; end if; end if; end process;
  22. Oh, and with regards to test benches. Somethings are hard to prove with test benches, and easy to prove in real life. Especially things that involve timescales needed for human interaction. Take the seven-segment display module. A 'full' test bench for that would be hard (test each digit for each value - 64 test patterns, each requiring at least enough time to update all output signals, 300 million cycles simulation cycles, and each blink test would need at least 64 million cycles for the counter to roll over - so half a billion simulation cycles. Then it would need to inspect the simulation output, to check that the correct LEDs were on at the correct time. Far easier to connect the digits to the switches on the dev board, the buttons to the blink inputs, download the design and manually verify it - a physical/manual test-bench :-) So I write VHDL testbenches when I know I will need to debug or otherwise inspect behavior of the internals. For a button debouncer my personal view is that a test bench is a bit over the top, and it is hard to have the correct assumptions of how bad a really bad switch is - and all my projects are for my own enjoyment, so I don't have to design to commercial standards of proof.
  23. @Tickstart Here's my equivalent of the 7-seg code you posted, just for comparison - it is just a hack, so the blink rate and update rates were just convenient, rather than to any hidden spec! library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity seven_seg is Port ( clk : in STD_LOGIC; segments : out STD_LOGIC_VECTOR (6 downto 0); seg_select : out STD_LOGIC_VECTOR (3 downto 0); digit0 : in STD_LOGIC_VECTOR (3 downto 0); blink0 : in STD_LOGIC; digit1 : in STD_LOGIC_VECTOR (3 downto 0); blink1 : in STD_LOGIC; digit2 : in STD_LOGIC_VECTOR (3 downto 0); blink2 : in STD_LOGIC; digit3 : in STD_LOGIC_VECTOR (3 downto 0); blink3 : in STD_LOGIC); end seven_seg; architecture Behavioral of seven_seg is signal digit_count : unsigned(25 downto 0); signal to_decode : std_logic_vector(3 downto 0); begin -- GFEDCBA with to_decode select segments <= "1000000" when "0000", -- Zero "1111001" when "0001", -- One "0100100" when "0010", -- Two "0110000" when "0011", -- Three "0011001" when "0100", -- Four "0010010" when "0101", -- Five "0000010" when "0110", -- Six "1111000" when "0111", -- Seven "0000000" when "1000", -- Eight "0011000" when "1001", -- Nine "0001000" when "1010", -- A "0000011" when "1011", -- B "1000110" when "1100", -- C "0100001" when "1101", -- D "0000110" when "1110", -- E "0001110" when others; -- F process(clk) begin if rising_edge(clk) then seg_select <= "1111"; -- active low! case digit_count(digit_count'high-6 downto digit_count'high-7) is when "00" => seg_select(0) <= blink0 AND digit_count(digit_count'high); to_decode <= digit0; when "01" => seg_select(1) <= blink1 AND digit_count(digit_count'high); to_decode <= digit1; when "10" => seg_select(2) <= blink2 AND digit_count(digit_count'high); to_decode <= digit2; when others => seg_select(3) <= blink3 AND digit_count(digit_count'high); to_decode <= digit3; end case; digit_count <= digit_count + 1; end if; end process; end Behavioral;
  24. 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.. )
  25. 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"