Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
13 years ago

Simple SPI shift register, mismatch between simulation and real behavior

This is a shot in the dark, but I have what should be an essentially very simple shift register design, that should accept a byte from the SPI peripheral of a microcontroller.

Ideally the device should work like this:

  • The microcontroller selects the register by bringing cs_power low

  • The microcontroller clocks out 8 bits. Data is asserted on spi_mosi on the falling edges of the clock, and data is read into the register on the rising edges

  • The microcontroller deselects the register by bringing cs_power high

  • On the deselct, the 8 data bits transmitted are latched into latch_reg. Bits are routed individually to other parts of the design for various functions (not shown here) - two of the bits, power_hv507 and ref_select are connected to bits 1 and 6 of that latch respectively. To test for proper operation of the register, I've connected these signals to pins on my MAXII CPLD, and am watching them on my oscilloscope.

I've compiled this design in Quartus II 9.1 (our current production version) and 12, to the same effect. The 9.1 simulator doesn't make much sense of the design, but using modelsim 10.0d with a testbench written for this purpose, the waveforms look exactly as I would expect. I can observe the data bits changing in the taps of the shift register, and the correct data byte is latched into latch_reg when cs_power is deselected. The routing of the latch outputs to their respective signals is correct, as observed in the simulator.

Loading the design into the CPLD however produces repeatable, but incorrect behavior. I'm only able to physically observe two bits of the latch_reg at a time (which I have wired by default to bits 1 and 6) - I can rearrange the bits of latch_reg I observe, but in none of the cases do asserting the bits of interest result in the correct outputs.

This should be very straightforward, but it's been vexing me for days. Is there something obvious I've done incorrectly here? The mismatch between the simulation and the actual behavior of the device (especially for something so simple) is particularly frustrating.


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity effector_module is
    port (
        spi_sclk      : in std_logic     := '0';
        spi_mosi      : in std_logic     := '0';
        
        cs_hv507      : in std_logic     := '1';
        cs_impedance  : in std_logic     := '1';
        cs_cart       : in std_logic     := '1';
        cs_vctl       : in std_logic     := '1';
        cs_feedback   : in std_logic     := '1';
        cs_hv507_clk  : in std_logic     := '1';
        cs_power      : in std_logic     := '1';
        hv507_fault   : in std_logic     := '0';   -- new 14
        err           : in std_logic     := '0';   -- new 12
        fault_cart    : in std_logic     := '0';   -- new 2
        timer         : inout std_logic  := '0';
        hv_suppress   : inout std_logic  := '0';   -- new 10
        power_cart    : inout std_logic  := '0';   -- new 3
        cart_present  : inout std_logic  := '0';   -- new 4
        blank         : inout std_logic  := '0';   -- new 5
        power_hv507   : out std_logic := '0';     -- new 13
        ref_select    : out std_logic := '0';     -- new 8
        not_hv_shdn   : out std_logic := '0';     -- new 9  
        bleed         : out std_logic := '0';     -- new 11
        cart_miso_dir : out std_logic := '0';     -- new 1
        pol           : out std_logic := '0';     -- new 6
        sck_hv507     : out std_logic := '0';     -- new 7
        spi_miso      : out std_logic := 'Z';
        tapsout       : out std_logic_vector (7 downto 0)
        );
end effector_module;
library ieee;
use ieee.std_logic_1164.all;
entity basic_shift_register is
    generic
    (
        NUM_STAGES : natural := 256
    );
    port 
    (
        clk        : in std_logic;
        enable    : in std_logic;
        sr_in    : in std_logic;
        sr_out    : out std_logic;
        taps    : inout std_logic_vector ((NUM_STAGES-1) downto 0)
    );
end entity;
-- Shift register with active low enable
architecture rtl of basic_shift_register is
    -- Build an array type for the shift register
    -- Declare the shift register signal
    begin
        process (clk, enable, taps)
        begin
            if (enable = '1') then
                taps((NUM_STAGES-1) downto 0) <= taps((NUM_STAGES-1) downto 0);
            elsif (rising_edge(clk)) then
                -- Shift data by one stage; data from last stage is lost
                taps((NUM_STAGES-1) downto 1) <= taps((NUM_STAGES-2) downto 0);
                -- Load new data into the first stage
                taps(0) <= sr_in;
            end if;
        end process;
        -- Capture the data from the last stage, before it is lost
        sr_out <= taps(NUM_STAGES-1);
end rtl;
architecture rtl of effector_module is 
    
    signal delta    : std_logic;
    signal clear_sticky : std_logic;
    signal dc_mode : std_logic;    
    signal pwr : std_logic_vector(7 downto 0);
    signal latch_reg : std_logic_vector(7 downto 0);
    begin
    power_hv507 <= pwr(1);
    ref_select <= pwr(6);        
    
    power_sr : entity work.basic_shift_register(rtl)
        generic map( NUM_STAGES => 8)        
        port map(
            clk => spi_sclk,
            enable => cs_power,
            sr_in => spi_mosi,
            taps => pwr
                );
    process (cs_power, pwr)
    begin
        if rising_edge(cs_power) then
            latch_reg <= pwr;
        end if;
    end process;
end rtl;

6 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    its probably the asynchronous enable.

    In simulation, it will only shift when enable goes high (eg on the rising edge), because of the way VHDL works but in the real hardware it will just shift whenever enable is one, so not on the rising edge. Sensitivity lists are ignored for synthesis and so you will have a shift register that is shifting whenever enable = '1' ie. do as many shifts as you can in infinitely small amount of time.

    Why do you have an async enable?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    The enable came with the Altera template code that I dropped in to do the shift register in the first place, so I left it there. I don't strictly need an enable at all, since I latch the output on my select, but it seemed like a better design not to be shifting except for when the select (enable) was low.

    I still don't follow. Even with the asynchronous enable, I still should not shift except for on rising edges of the clock, right? That's what the elsif clause says should happen? (if enable = 1, do nothing, if enable = 0 AND rising edge of clock, shift down) What's my alternative to an async enable, other than omitting it altogether? (or is omitting it altogether the thing to do?)
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    After having removed the enable (simply always clocking data on the rising edge of the clock) the behavior is the same. One thing worth noting is that if I connect my observable pins to latch_reg(1) and latch_reg(6) - the bits that actually make them go high are bits 0 and bits 3, respectively. That is, if I shift out 0x02 or 0x40, I don't see either of my bits come on. If I shift out a 0x01 or an 0x08 however, I see the bits turn on in their respective locations

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Ok, sorry about that - must admit I only scanned it the first time - you're correct about the enable.

    But I do question why taps is an inout - why isnt it an internal signal, or just an output?

    Secondly are you sure the problem isnt related to using two different clocks? what is this cs_power? is it a real clock?
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    No worries! I appreciate your help.

    cs_power isn't a clock, per se, it's a select signal. This is connected to the SPI port on a microcontroller. The idea is that the microcontroller selects the CPLD by bringing cs_power low, it clocks out the 8 bits (using sck for clock and mosi for data) and then deselects the CPLD by bringing cs_power high again. On the rising edge of the deselect the contents of the taps are latched into latch_reg.

    taps is declared as an inout so I can read it inside the process to do the shifting. There might be a better way to do it?

    http://en.wikipedia.org/wiki/serial_peripheral_interface_bus#clock_polarity_and_phase

    The diagram at the above link gives the general sense of how this should work. sr_in = MOSI, clk = sck and SS = cs_power

    Thank you so much for your help. I'm on a deadline here, and I've worked myself into a corner, I'm afraid!
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    1. CS_POWER isn't a clock.

    BASIC_SHIFT_REGISTER latches TAPS using the SPI_CLK, which is then assigned to PWR.

    process (cs_power, pwr)
        begin
            if rising_edge(cs_power) then
                latch_reg <= pwr;
            end if;
        end process;

    This code generates a D-flipflop using CS_POWER as a clock input. Unless you can guarantee that the CS_POWER clock edges are synchronized with the SPI_CLK, you will have potential metastability problems due to setup/hold violations on this flipflop.

    I would recommend hitting up the fpga4fun.com website and looking at their clock crossing articles (including flag detection/generation). Once you have the SPI byte you are going to need to cross to your internal clock domain to do any processing. You need to generate a 1 internal-clock cycle pulse/flag that detects the low-hi transition on CS_POWER and use it as an enable to your latch (along with appropriate synchronizers).