3

Warning: this is going to be long. Sorry if it's too verbose.

I'm just starting out on learning FPGAs and VHDL using Quartus Prime. Over the past few days I've taught myself:

  • How to write VHDL
  • How to make a component
  • How to write a testbench
  • How to use previously created and tested components - knitted together - to create a new component

What I can't work out though is how I would create a testbench that tests a new component that uses two existing components, when some of the signals that are in this new component are only internal signals.

So, here are two super-simple components that I have successfully written and tested with test benches. I realise this is not real world by the way, I'm just trying to take baby steps.

1. A four bit register

library ieee;
use ieee.std_logic_1164.all;

entity four_bit_reg is
port
    (
    bcd_in: in std_logic_vector(3 downto 0);
    clk: in std_logic;
    clr: in std_logic;
    bcd_out: out std_logic_vector(3 downto 0)
    );

end four_bit_reg;

architecture behaviour of four_bit_reg is
begin
    process (clk,clr)
        begin
        if (clr = '1') then
            bcd_out <= "0000";
        elsif rising_edge(clk) then
            bcd_out <= bcd_in;
        end if;
    end process;

end behaviour;

2. A BCD to seven segment converter

library ieee;
use ieee.std_logic_1164.all;

entity sev_seg is
    port
        (
        bcd_value : in std_logic_vector(3 downto 0);
        sev_seg_value : out std_logic_vector(6 downto 0)                                        
        );

end sev_seg;

architecture behaviour of sev_seg is
begin   
    sev_seg_process : process (bcd_value)
        begin
        case bcd_value is
            when "0000" => sev_seg_value <="0111111"; -- 0
            when "0001" => sev_seg_value <="0000110"; -- 1
            when "0010" => sev_seg_value <="0111011"; -- 2
            when "0011" => sev_seg_value <="1001111"; -- 3
            when "0100" => sev_seg_value <="1100110"; -- 4
            when "0101" => sev_seg_value <="1101101"; -- 5
            when "0110" => sev_seg_value <="1111101"; -- 6
            when "0111" => sev_seg_value <="0000111"; -- 7
            when "1000" => sev_seg_value <="1111111"; -- 8
            when "1001" => sev_seg_value <="1101111"; -- 9
            when others => sev_seg_value <= "0000000"; -- A to F should show blank      
        end case;
    end process sev_seg_process;
end behaviour;

First question: What do you call the two things above? Components? Modules? Entities? Something else?

I then use these two in another new component/entity/module (as applicable) as below:

library ieee;
use ieee.std_logic_1164.all;

entity two_modules is
    port
        (
        bcd_pins : in std_logic_vector(3 downto 0);
        sev_seg_pins : out std_logic_vector(6 downto 0)                     
        );

end two_modules;


architecture behaviour of two_modules is
-- Internal signals
signal int_clk: std_logic;
signal int_bus: std_logic_vector(3 downto 0); 

-- List any components used in the design
    component four_bit_reg is
        port
            (
            bcd_in: in std_logic_vector(3 downto 0);
            clk: in std_logic;
            clr: in std_logic;
            bcd_out: out std_logic_vector(3 downto 0)
            );
    end component;

    component sev_seg is
        port
            (
            bcd_value : in std_logic_vector(3 downto 0);
            sev_seg_value : out std_logic_vector(6 downto 0)                                        
            );

    end component;

begin -- start the instances
    fbr: four_bit_reg port map
        (
        clk => int_clk,
        bcd_in => bcd_pins,
        clr => '0',
        bcd_out => int_bus
        );
    sseg: sev_seg port map
        (
        bcd_value => int_bus,
        sev_seg_value => sev_seg_pins

        );

end behaviour;      

So, for this thing I have called two_modules, the framework for the test bench created by Quartus is as follows:

LIBRARY ieee;                                               
USE ieee.std_logic_1164.all;                                

ENTITY two_modules_vhd_tst IS
END two_modules_vhd_tst;
ARCHITECTURE two_modules_arch OF two_modules_vhd_tst IS
-- constants       

-- signals                                                   
SIGNAL bcd_pins : STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL sev_seg_pins : STD_LOGIC_VECTOR(6 DOWNTO 0);
signal internal_clock : std_logic := '0';
COMPONENT two_modules
    PORT (
    bcd_pins : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
    sev_seg_pins : OUT STD_LOGIC_VECTOR(6 DOWNTO 0)
    );
END COMPONENT;
BEGIN
    i1 : two_modules
    PORT MAP (
-- list connections between master ports and signals
    bcd_pins => bcd_pins,
    sev_seg_pins => sev_seg_pins
    );

internal_clock <= not internal_clock after 500 us;

init : PROCESS                                               
-- variable declarations                                     
BEGIN                                                        
        -- code that executes only once                      
WAIT;                                                       
END PROCESS init;                                           
always : PROCESS                                              
-- optional sensitivity list                                  
-- (        )                                                 
-- variable declarations                                      
BEGIN                                                         
        -- code executes for every event on sensitivity list  
WAIT;                                                        
END PROCESS always;                                          
END two_modules_arch;

As you can see I have created an internal clock and I would like to, purely for the purposes of learning how to do this type of thing, I stress I realise this is not a complete design, join the internal_clock (that I can see works and is a waveform in the waveform editor of Model Sim) to clk in the four_bit_reg.

I think and hope once I know how to do this I'll be able to plough on and get a real world, more complicated test bench knocked up. However, after much Googling I can find no reference on how to bind together signals from subcomponents. This may be because I am using completely the wrong terminology and there may be a perfect tutorial somewhere out there.

So:

  1. How can I just for a start get my internal_clock connected to subcomponent, four_bit_reg's clk input?
  2. What is the correct teminology for when you use and knit together things like four_bit_reg and sev_seg? Subcomponents? Something else?

Many thanks if you got this far!

DiBosco
  • 838
  • 8
  • 21

3 Answers3

3

With the comments, I understand that you are using an internal oscillator from Altera in your CPLD.

I suggest to add a third module named "internal_oscillator" which can be described as follow :

library ieee;
  use ieee.std_logic_1164.all;

entity internal_oscillator is
port (
  CLK  : out std_logic
);
end entity;

architecture for_simulation_only of internal_oscillator is
  constant C_HALF_PERIOD : time      := 5 ns; -- 100MHz
  signal   clk_internal  : std_logic := '0';
begin

  clk_internal <= not clk_internal after C_HALF_PERIOD;
  CLK <= clk_internal;

end architecture;

You can now add this module in your design and you'll get a clock without adding a new pin on your top level entity :

osc_inst : entity work.internal_oscillator 
port map (CLK => int_clk);
grorel
  • 1,408
  • 15
  • 21
  • As pointed by @Oldfart, there must be a simulation model (BFM) for the internal_oscillator. This way, you have a simple working simulation clock without dealing with Altera BFM which can be difficult to use for beginners even if the good way would be to use it. Next step ;) – grorel Jul 04 '18 at 14:43
  • Sorry if this is a dumb question. How do the signal from osc_inst get to CLK? Is osc_inst used at all? I mean, I expected something like `port map (CLK => osc_inst);` – chick3n0x07CC Aug 03 '23 at 12:07
1

In your two_models entity, add a new port for the clock signal:

entity two_modules is
    port
        (
        clk : in std_logic;
        bcd_pins : in std_logic_vector(3 downto 0);
        sev_seg_pins : out std_logic_vector(6 downto 0)                     
        );

end two_modules;

Remove the int_clk signal in the two_models architecture. Replace it with the previously defined input signal instead when you are connecting the submodules:

fbr: four_bit_reg port map
    (
    clk => clk_in,
    bcd_in => bcd_pins,
    clr => '0',
    bcd_out => int_bus
    );

In your testbench, connect the internal clock signal internal_clock into that port of the two_modules:

    PORT MAP (
-- list connections between master ports and signals
    clk_in => internal_clock,
    bcd_pins => bcd_pins,
    sev_seg_pins => sev_seg_pins
    );
am9417
  • 994
  • 8
  • 18
  • I had already done this following @grorel 's suggestion and that worked (although clk : in std_logic; I did as master_clk: in std_logic; and in the fbr instantiation I did clk => master_clk, ). Although this works, what I am not getting about this is that I *think* that means I have an external clock pin on my CPLD and I was wanting to use the internal oscillator. Is my assumption wrong? – DiBosco Jul 04 '18 at 12:55
  • Well, this approach is solely for simulating purposes, the simulated clock has to be somehow routed from the test bench into the DUT to drive the internal clock. I think in your final design you can then remove this port from your module if you don't need it. – am9417 Jul 04 '18 at 13:03
  • 1
    Fair enough! I'll carry on building up my design and see if this helps. Thanks! – DiBosco Jul 04 '18 at 13:05
1

In most cases the clock is an input to the module. Often accompanied by a reset.

If you look around on the www for example VHDL code you will notice that every module, has a clock input.

There are general two exceptions:

  1. Test-benches generate an artificial clock inside to drive the Device Under test.
  2. Modules which simulate a real clock generating circuit e.g. a Crystal oscillator.
Oldfart
  • 6,104
  • 2
  • 13
  • 15
  • In this case I indeed want to use the internal oscillator for my end design. Grorel's suggestion works, but as I said in reply to am9, I believe I now have an unwanted pin on my CPLD for master_clk. Maybe that is a big misapprehention and/or maybe I can declare "entity two_modules is port (" with master_clk as something other than std_logic that means I don't have an extra pin. – DiBosco Jul 04 '18 at 13:02
  • @DiBosco : Your internal oscillator is a vendor related entity. You choose the quartus tag, you'd be using Altera CPLD. In quartus, look at the "IP Catalog" and search for "Oscillator", you'll find your "internal oscillator" entity. – grorel Jul 04 '18 at 13:29
  • 2
    If your circuit has an internal oscillator, you should try to find the code. Every FPGA vendor I know provides simulation models for their IP. But I must warn you: getting those to work often requires more then just beginners knowledge of the language. – Oldfart Jul 04 '18 at 13:29
  • 1
    @grorel Yes, I have found the internal oscillator code and incorporated it into a [different, more complex] design a few days ago that I have compiled, but not simulated. I am still not clear how I'll simulate that at the moment. However, for this question I was taking the smallest possible steps to simulate just two components incorporated into one design. I probably have some muddled thinking going on here. There is a *lot* to learn here just by finding tutorials on line and I'm likely doing some badly thought out things at times. The simulating I find harder than the design! – DiBosco Jul 04 '18 at 14:01