-->

VHDL (Xilinx toolchain) I'm being scuppered by

2019-08-05 05:05发布

问题:

I've got a two-file VHDL project that I'm having beginner's difficulties with.

It takes the system clock and use a 30-bit clock divider (of which I'm only using a small number of non-consecutive bits) to drive a primitive serial port module (outgoing TX only) module to spit out 8 bit characters periodically.

It seems that during the synthesis process, many of the essential signals are being removed by the optimizer, which I didn't expect.

The top level file "Glue.vhd"...

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use ieee.std_logic_unsigned.all;

entity Glue is
    port(
        clk : in std_logic;
        tx  : out std_logic;
        LED : out std_logic_vector(1 downto 0)
        );
end entity Glue;

architecture behavioural of Glue is
    signal divider : unsigned(29 downto 0);
begin
    LED(1) <= '0';

    ser_tx : entity SerialTX
    port map (
            baud_clk => divider(12),
            byte_to_transmit => std_ulogic_vector(divider(29 downto 22)),
            poke => divider(20),
            busy => LED(0),
            serial_out => tx
            );

    clocker : process(clk)
    begin
        IF(rising_edge(clk)) then
            divider <= divider + 1;
        END IF;
    end process clocker;
end architecture behavioural;

SerialTX.vhd

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity SerialTX is
    port (
        baud_clk         : in  std_logic;
        byte_to_transmit : in  std_ulogic_vector(7 downto 0); --the byte that we want to transmit
        poke             : in  std_logic;                     --a rising edge causes the byte to be sent out
        busy             : out std_logic;                     --wait for this to go low before transmiting more data
        serial_out       : out std_logic                      --the RS232 serial signal
        );
end SerialTX;

architecture behavioural of SerialTX is
    signal bit_buf                : unsigned(9 downto 0); --(STOP bit) & (8 data bits) & (START bit)
    signal internal_busy          : std_logic;
    shared variable bit_counter   : integer range 0 to 10;
begin
    busy <= internal_busy;

    busy_handler : process(poke) is
    begin
        if(rising_edge(poke)) then
            internal_busy <= '1';
        end if;
        if(bit_counter = 0) then
            internal_busy <= '0';
        end if;
    end process busy_handler;

    do_transmit : process(baud_clk) is
    begin
        if(rising_edge(baud_clk)) then 
            if((internal_busy = '1') and (bit_counter = 0)) then
                bit_counter := 10;
                bit_buf <= unsigned('1' & byte_to_transmit & '0');
            end if;

            serial_out <= bit_buf(0);
            bit_buf <= bit_buf srl 1;
            bit_counter := bit_counter - 1;
        end if;
    end process do_transmit;
end behavioural;

The warnings (no errors mind you) from the synthesis process are as follows...

WARNING:Xst:647 - Input <byte_to_transmit> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:1710 - FF/Latch <bit_buf_9> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_8> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_7> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_6> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_5> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_4> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_3> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_2> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_1> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_0> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <serial_out> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:2404 -  FFs/Latches <bit_buf<9:0>> (without init value) have a constant value of 0 in block <SerialTX>.
WARNING:Xst:1710 - FF/Latch <serial_out> (without init value) has a constant value of 0 in block <SerialTX>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:2677 - Node <divider_21> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_22> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_23> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_24> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_25> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_26> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_27> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_28> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_29> of sequential type is unconnected in block <Glue>.
WARNING:Route:455 - CLK Net:divider<20> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.
WARNING:Route:455 - CLK Net:divider<12> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.

I've traced through the connections in the source code and I cannot find the mistakes that I'm making. I get the feeling that I'm missing some edge/corner cases that I've not covered in the assignments.

The items marked "(without init value)" I have tried to rectify by giving them default values to no avail. The ones marked as "unconnected in block " are bewildering.

What must I do to satisfy the synthesizer?

回答1:

You can simulate your design, it's on the edge of complexity where it would be easier than guessing why it doesn't work when synthesized and loaded in an FPGA. It can also be made simpler.

It's possible to get rid of bit_counter because you have a 10 bit bit_buf shift register.

The way this would work, is that you set a default pattern that is recognized as 'empty', and that pattern is constructed as a result of shifting bit_buf out.

The pattern doesn't occur other than as an artifact of shifting out with a 'left' shift in of '0'. The shift register shifts '0's in and stops when the stop bit is in the right most position ("0000000001"). The '1' in the right had position maintains the transmit idle mark.

The idea of the idle pattern comes from your use of srl which left fills with '0's, noticed while debugging your original design. The amount of incremental changes while 'learning' why you designed this the way you did became prohibitive. It lead to looking at first principles and describing an implementation based on yours from that.

To simulate your design to begin with there are changes to Glue including an initial value for divide so the increment works, and moving the divider tap-offs for poke and byte_to_transmit, to reduce the simulation time to the neighborhood of 40 ms for the following.

The instantiation of SerialTx uses a selected name, simulators don't include a use work.all; context item implicitly, as is sometimes provided in synthesis tools.

A mark up of your design with an added testbench, without bit_counter:

library ieee;
use ieee.std_logic_1164.all;
-- use ieee.numeric_std.all;

entity SerialTX is
    port (
        baud_clk:          in  std_logic;
        byte_to_transmit:  in  std_logic_vector(7 downto 0); -- before -2008
        poke:              in  std_logic;
        busy:              out std_logic;
        serial_out:        out std_logic
    );
end entity SerialTX;

architecture foo of SerialTX is
    -- signal bit_buf:         unsigned(9 downto 0);
    constant BB_IDLE:       std_logic_vector (9 downto 0) := "0000000001";
    signal bit_buf:         std_logic_vector (9 downto 0) := BB_IDLE;
    -- signal internal_busy:   std_logic;
    signal poke_reg:        std_logic_vector (0 to 2) := "000";
    signal start_tx:        std_logic;

    -- shared variable bit_counter:    integer range 0 to 10;
begin
    -- busy <= internal_busy;
poke_filt:
    process (baud_clk)  -- translate poke to baud_clk domain
    begin
        if rising_edge (baud_clk) then
            poke_reg <= poke & poke_reg (0 to 1);
        end if;
    end process;

    -- front edge of poke in baud_clk_domain:
    start_tx <= poke_reg(0) and poke_reg(1) and not poke_reg(2);

-- busy_handler:
--     process (poke) is
--     begin
--         if rising_edge (poke) then
--             internal_busy <= '1';
--         end if;
--         if bit_counter = 0 then
--             internal_busy <= '0';
--         end if;
--     end process busy_handler;

busy_handler:  
    process (baud_clk)
    begin
        if rising_edge (baud_clk) then
            if start_tx = '1' and bit_buf = BB_IDLE then
                busy <= '1';
            elsif bit_buf = BB_IDLE then
                busy <= '0';
            end if;
        end if;
    end process;

do_transmit:
    process (baud_clk)
    begin
        if rising_edge(baud_clk) then 
            if start_tx = '1'  and bit_buf = BB_IDLE then
                bit_buf <=  '1' & byte_to_transmit & '0';
            elsif bit_buf /= BB_IDLE then
                -- bit_buf <= bit_buf srl 1; 
                -- srl UNDEFINED in package std_logic_1164
                bit_buf <= '0' & bit_buf(9 downto 1); -- shift right one
            end if;                                   -- zero fill
        end if;
    end process;

-- do_transmit:
--     process (baud_clk)
--     begin
--         if rising_edge(baud_clk) then
--             if internal_busy = '1' and bit_counter = 0 then
--                 bit_counter := 10;
--                 bit_buf <= unsigned ('1' & byte_to_transmit & '0');
--             end if;
--             serial_out <= bit_buf(0);
--             bit_buf <= bit_buf srl 1;
--             bit_counter := bit_counter - 1;
--         end if;
--     end process do_transmit;

    serial_out <= bit_buf(0); -- ADDED, no 11th flip flop

end architecture;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- use ieee.std_logic_unsigned.all;

entity Glue is
    port (
        clk:  in  std_logic;
        tx:   out std_logic;
        LED:  out std_logic_vector(1 downto 0)
    );
end entity Glue;

architecture behavioural of Glue is
    signal divider:  unsigned(29 downto 0) := (others => '0'); -- init val
begin
    LED(1) <= '0';

ser_tx:  
    entity work.SerialTX  -- ADDED work prefix to make selected name
        port map (
            baud_clk => divider(12),
         -- byte_to_transmit => std_ulogic_vector(divider(29 downto 22)),
            byte_to_transmit => std_logic_vector(divider(25 downto 18)),
            poke => divider(17), -- WAS divider(20), for simulation
            busy => LED(0),
            serial_out => tx
        );
clocker:  
    process (clk)
    begin
        if rising_edge(clk) then
            divider <= divider + 1;
        end if;
    end process clocker;
end architecture behavioural;

library ieee;
use ieee.std_logic_1164.all;

entity glue_tb is
end entity;

architecture fum of glue_tb is
    signal clk:     std_logic := '0';
    signal tx:      std_logic;
    signal led:     std_logic_vector(1 downto 0);
begin
DUT:
    entity work.glue
        port map (
            clk => clk,
            tx => tx,
            led => led
        );
CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        if now > 40 ms then
            wait;
        end if;
    end process;
end architecture;

Running for 40 ms gives:

A close up of byte_to_transmit value 05:

You could note srl is not provided in package std_logic_1164, and has been replaced as has the use of std_ulogic_vector, std_logic_vector became it's subtype in -2008.

poke is filtered into the baud_clk domain and generates an single clock period event the sets internal_busy to '1', which is subsequently set to '0' when the idle pattern is again detected in bit_buf.

Note busy is '1' for 10 clocks (start bit, 8 data bits, and stop bit) and the 11th flip flop for serial_out has been dropped.

This was simulated using ghdl, binaries can be downloaded here. The waveform displays are from gtkwave. Both are licensed under the GPL.

VHDL has a specific set of language constructs for inferring sequential logic - memories, registers and latches, part of a subset of the language useful in synthesis. A superset or subset of eligible VHDL is typically supported by a synthesis vendor who might also include restrictions based on their target platform silicon. Eligibility and supported constructs are found in synthesis vendor documentation. The VHDL language itself is defined in IEEE Std 1076-2008, although universal support is only found for the -1993 or -2002 revision.

One of the things you'll find is that shared variables have become protected types with access through methods, insuring exclusive access to shared resources and incidentally becoming ineligible for synthesis.

Am I right in thinking that your 3-bit poke_reg system means that the poke high pulse duration must be longer than 3 baud_clk edges in order to be correctly detected? Is it possible to make the poke signal edge-triggered? – Wossname 13 hours ago

scary_jeff 's method will work for poke (send) events shorter than or equal to three baud_clk intervals.

You don't use busy (internal_busy) in SerialTx and it only covers the interval of baud_clk periods (bauds) during actual transmission.

It's possible to create a busy signal that is safe (the xor of two flip flops) and works for the entire interval between the rising edge of poke (send) and the end of internal_busy:

architecture fum of SerialTX is
    constant BB_IDLE:       std_logic_vector (9 downto 0) := "0000000001";
    signal bit_buf:         std_logic_vector (9 downto 0) := BB_IDLE;
    signal poke_event:      std_logic := '0'; -- Added
    signal internal_busy:   std_logic := '0'; -- Re-Added
    signal poke_reg:        std_logic_vector (0 to 1) := "00";
    signal start_tx:        std_logic;
    signal end_event:       std_logic := '0'; -- Added
begin

    busy <= poke_event xor end_event;  -- ADDED, was FF output

pokeevent:
    process (poke)
    begin
        if rising_edge(poke) then
            poke_event <= not poke_event;
        end if;
    end process;
poke_edge:
    process (baud_clk)  -- translate poke to baud_clk domain
    begin
        if rising_edge (baud_clk) then
            poke_reg <= poke_event & poke_reg (0);
        end if;
    end process;

    -- front edge of poke in baud_clk_domain:
    start_tx <= poke_reg(0) xor poke_reg(1); -- CHANGED, when not equal

endevent:
    process (baud_clk)
    begin
        if rising_edge (baud_clk) then
            if internal_busy = '1' and bit_buf = BB_IDLE then
                end_event <= not end_event;
            end if;
        end if;
    end process;

busy_handler:  -- CHANGED
    process (baud_clk)
    begin
        if rising_edge (baud_clk) then
            if start_tx = '1' and bit_buf = BB_IDLE then
                internal_busy <= '1';
            elsif bit_buf = BB_IDLE then
                internal_busy <= '0';
            end if;
        end if;
    end process;

do_transmit:
    process (baud_clk)
    begin
        if rising_edge(baud_clk) then 
            if start_tx = '1'  and bit_buf = BB_IDLE then
                bit_buf <=  '1' & byte_to_transmit & '0';
            elsif bit_buf /= BB_IDLE then
                bit_buf <= '0' & bit_buf(9 downto 1);
            end if;
        end if;
    end process;

    serial_out <= bit_buf(0);

end architecture;

This gives:

Note that the internal_busy signal is used to determine when the back edge of busy occurs (distinguishing between the idle pattern in bit_buf when transmitting or not).

The poke signal has been shortened by modifying Glue to prove the width of poke (send) can be arbitrary:

architecture behavioural of Glue is
    signal divider:  unsigned(29 downto 0) := (others => '0'); -- init val
    signal poke:    std_logic := '0';  -- ADDED
begin
    LED(1) <= '0';

    poke <= divider(17), '0' after 10 us;  -- ADDED
ser_tx:  
    entity work.SerialTX  -- ADDED work prefix to make selected name
        port map (
            baud_clk => divider(12),
            -- byte_to_transmit => std_ulogic_vector(divider(29 downto 22)),
            byte_to_transmit => std_logic_vector(divider(25 downto 18)),-- sim
            poke => poke, -- WAS divider(20), for simulation
            busy => LED(0),
            serial_out => tx
        );

(No, that complex waveform representing a singleshot on the new signal poke is not synthesis eligible.)



回答2:

While user1155120 is right in the sense that you should either specify the library or do component binding, your tools very obviously found the right entity, because that's where your logic is being optimized.

First things first, don't use

use IEEE.numeric_std.all;
use ieee.std_logic_unsigned.all;

together. You should use numeric_std except when working with legacy code.

The real problem is two-fold:

  • You don't initialize your counter variable and it doesn't count right.
  • Your input data never makes it into the parallel-load shift register and is therefore trimmed.

At what value would you like your counter to start? IIRC, uninitialized integer types will assume the lowest valid value in their range. However, it's better to make the start explicit in any case. However, in reality there are no integers in FPGAs - just cold, hard flip flops. A range of 0 to 10 uses at least 4 bits and hence 4 flip flops. However, going from 0 to "-1" will underflow and wrap around to 15 instead of the 10 you might expect. internal_busy is intended to be a flip flop but not initialized either. I say intended, because it's actually asynchronously reset by bit_counter. This asynchronous reset in itself is problematic and almost certainly not what you intended:

busy_handler : process(poke) is -- <-- wrong sensitivity list
begin
    if(rising_edge(poke)) then
        internal_busy <= '1';
    end if;
    if(bit_counter = 0) then   -- <-- not clocked
        internal_busy <= '0';
    end if;
end process busy_handler;

You mixed clocked and combinational code. Your simulation will be off, because while the anding of internal_busy will be parallel in hardware, your simulation tool will only show updates on changes of poke.

-- can never happen, because bit_counter = 0 means internal_busy is forced to '0'
if((internal_busy = '1') and (bit_counter = 0)) then
    bit_counter := 10;
    bit_buf <= unsigned('1' & byte_to_transmit & '0');
end if;

Without this logic, bit_buf will indeed be 9'h000 (a value the Xilinx tools assume -- you didn't give any!). This is what the following lines try to tell you:

WARNING:Xst:1710 - FF/Latch <bit_buf_9> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_8> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_7> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_6> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_5> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_4> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_3> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_2> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_1> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1895 - Due to other FF/Latch trimming, FF/Latch <bit_buf_0> (without init value) has a constant value of 0 in block <ser_tx>. This FF/Latch will be trimmed during the optimization process.
WARNING:Xst:1710 - FF/Latch <serial_out> (without init value) has a constant value of 0 in block <SerialTX>. This FF/Latch will be trimmed during the optimization process.
  1. bit_buf_9 is constantly zero and will be replaced with a hard zero (logic or fabric). Therefore its flip flop is redundant and can be trimmed.
  2. bit_buf_{8..0} were determined to be equivalent to some other flip flop (here: bit_buf_9). However, that flip flop got trimmed, so therefore, these flip flops will be trimmed too.
  3. bit_buf_9 feeds serial_out, so it, too, gets trimmed.

The rest of the error messages:

WARNING:Xst:647 - Input <byte_to_transmit> is never used. This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved.
WARNING:Xst:2677 - Node <divider_21> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_22> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_23> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_24> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_25> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_26> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_27> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_28> of sequential type is unconnected in block <Glue>.
WARNING:Xst:2677 - Node <divider_29> of sequential type is unconnected in block <Glue>.
  1. byte_to_transmit is unused and not part of a top-level block, it will not be preserved.
  2. divider(29 downto 22) is connected to byte_to_transmit. Since that port will be removed, all flip flip flops following the last used flip flop, divider(20) can be optimized away.

Clock warnings:

WARNING:Route:455 - CLK Net:divider<20> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.
WARNING:Route:455 - CLK Net:divider<12> may have excessive skew because     0 CLK pins and 1 NON_CLK pins failed to route using a CLK template.
  1. divider(20) and divider(12) are used as clocks.
  2. divider(20) and divider(12) are generated from logic: LUTs and flip flops inside one or more slices. Depending on the FPGA, there are a few that can be fed back to clock routing resources.
  3. In your case, the tools didn't find a way to route these signals on clock routing resources and had to use resources normally reserved for logic signals, hence skew might be excessive.

These clocking errors can be avoided by 1. using a common clock clk for both modules and 2. using poke and baud_clk as clock enables instead:

clk_en_p: process (clk) is
begin

if (rising_edge(clk)) then
    if (clk_en = '1') then
        my_signal <= assignment;
    end if;
end if;
end process clk_en_p;

All in all, you should write a testbench and simulate your design prior to synthesis as Brian Drummond suggested.

It often helps to write parts of a testbench before writing the implementation, because it will force you to think about the interface of your component and how you expect it to react prior to actually writing it.