0
\$\begingroup\$

I have written into a procedure the behavior of a simple SPI transaction that I use in my testbenches to make things easier to write and read. Is there a way to make this procedure as generic as possible by not mentioning the size of the vector data given as a parameter? Or maybe add a size parameter as an integer?

Here's the procedure I'm using so far. For now I have one procedure declaration for each vector size I want to transfer.

procedure spi_transfer_3B(data : std_logic_vector(23 downto 0)) is 
variable bitCnt : integer := data'length-1;
begin
    spi_clk <= '0';
    spi_mosi <= 'Z';
    spi_ss <= '0';
    wait until rising_edge(clk);

    for i in 2*data'length-1 downto 0 loop
        wait for SPI_CLK_PERIOD/2;
        spi_clk <= not spi_clk;
        if spi_clk = '0' then
            spi_mosi <= data(bitCnt);
            bitCnt := bitCnt -1;
        end if;
    end loop;

    spi_clk <= '0';
    wait until rising_edge(clk);
    spi_ss <= '1';
    spi_mosi <= 'Z';
end spi_transfer_3B;

What do you think?

\$\endgroup\$
3
  • \$\begingroup\$ Have you tried removing the range specification in the first line and seeing if it works for different widths of data? \$\endgroup\$ – scary_jeff Aug 3 '16 at 9:45
  • \$\begingroup\$ Yes, it is somewhat working, but not really. spi_transfer(x"45") gives me "A2" after deserializing the MOSI line. But it is probably the way to go, I'm trying to find the tweaking needed here \$\endgroup\$ – Fluffy Aug 3 '16 at 9:58
  • \$\begingroup\$ Oh... 0x45 = 0b01000101 and 0xA2 = 0b10100010. It's inverted, okay \$\endgroup\$ – Fluffy Aug 3 '16 at 10:01
2
\$\begingroup\$

This seems to work. I was confused because not mentioning x downto x coded the vector LSB first instead of MSB first as intended in my first snippet of code. It is that easy.

procedure spi_transfer(data : std_logic_vector) is 
variable bitCnt : integer := 0;
begin
    spi_clk <= '0';
    spi_mosi <= 'Z';
    spi_ss <= '0';
    wait until rising_edge(clk);

    for i in data'range loop
       wait for SPI_CLK_PERIOD/2;
       spi_clk <= '1';
       spi_mosi <= data(i);
       wait for SPI_CLK_PERIOD/2;
       spi_clk <= '0';
    end loop;

    spi_clk <= '0';
    wait until rising_edge(clk);
    spi_ss <= '1';
    spi_mosi <= 'Z';
end spi_transfer;
\$\endgroup\$
4
  • 2
    \$\begingroup\$ You can simplify and eliminate bitCnt to i, by reversing the range of the loop for i in data'range loop ... (or reverse_range as appropriate), and a second wait and spi_clk assignment, instead of doubling the range and using the if statement. \$\endgroup\$ – user_1818839 Aug 3 '16 at 10:54
  • \$\begingroup\$ Good call, it looks better this way. I'm editing my answer with your simplification. Thanks! \$\endgroup\$ – Fluffy Aug 3 '16 at 12:52
  • \$\begingroup\$ .... closer ... but now you can (and probably must!) eliminate the "if" too, and you can explicitly set spi_clk to '0' or '1' in each assignment. \$\endgroup\$ – user_1818839 Aug 3 '16 at 13:40
  • 1
    \$\begingroup\$ Edited again, thanks! I didn't read your first comment carefully enough, my bad \$\endgroup\$ – Fluffy Aug 3 '16 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.