· 

ChatGPTで「SPIハードをFPGAで」を再構築(2)

前回のつたない指示を反省してもう一度やってみる。

please create an example of spi ip using vhdl.

--------------------------

Sure, here is an example of an SPI (Serial Peripheral Interface) IP using VHDL:

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

entity spi_master is
  port (
    clk: in std_logic;
    reset: in std_logic;
    mosi: out std_logic;
    miso: in std_logic;
    cs: out std_logic;
    data_out: out std_logic_vector(7 downto 0);
    data_in: in std_logic_vector(7 downto 0);
    sck_delay: in integer := 0;
    cs_delay: in integer := 0
  );
end spi_master;

architecture behavioral of spi_master is
  signal sck: std_logic;
  signal bit_count: integer := 0;
  signal data: std_logic_vector(7 downto 0) := (others => '0');
  signal cs_active: std_logic := '1';
  signal cs_delay_count: integer := 0;

begin

  process(clk, reset)
  begin
    if reset = '1' then
      sck <= '0';
      bit_count <= 0;
      cs_active <= '1';
      cs <= cs_active;
      cs_delay_count <= 0;
      data <= (others => '0');
    elsif rising_edge(clk) then
      if cs_delay_count < cs_delay then
        cs_delay_count <= cs_delay_count + 1;
        cs_active <= '1';
      else
        cs_active <= '0';
        cs <= cs_active;
        if sck_delay > 0 then
          wait for sck_delay * 1 ns;
        end if;
        if bit_count = 0 then
          mosi <= data(7);
        else
          mosi <= data(bit_count - 1);
        end if;
        if bit_count < 8 then
          bit_count <= bit_count + 1;
          data_out <= (others => '0');
        else
          data_out <= data;
          bit_count <= 0;
        end if;
        sck <= not sck;
      end if;
    end if;
  end process;

  process(clk)
  begin
    if rising_edge(clk) then
      if cs_active = '0' then
        data(bit_count - 1) <= miso;
      end if;
    end if;
  end process;

end behavioral;

In this example, the SPI master has inputs for the clock signal (clk), reset signal (reset), and inputs from the SPI slave (miso). It also has outputs for the MOSI signal (mosi), chip select signal (cs), and outputs to the user (data_out). The user can input data to be sent to the SPI slave via the data_in signal.

 

The SPI master is implemented using two processes. The first process handles the clock signal and drives the MOSI signal. It also outputs data to the user and increments a counter to keep track of the bit being transmitted. The chip select signal is also managed in this process, with a delay being applied to ensure that the signal is not activated before the specified delay. The second process listens for the MISO signal and updates the data being received from the SPI slave.

 

Note that this is just an example implementation and the actual design may vary based on the specific requirements and constraints of the system being developed.

だいぶコードの雰囲気が変わったし、指示してないのにSPI masterだけの実装になってます。前回と全く同じ指示で開始したんですが、、、担当者が変わったんですかね、、、

ではここから、送信ビット長の指示を入れていきます。

in your example, the bit length of spi communication is fixed to 8bits. could you please make it with  configurable bit length option.

--------------------------

Sure, here is an updated example of an SPI (Serial Peripheral Interface) IP using VHDL with a configurable bit length option:

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

entity spi_master is
  generic (
    DATA_WIDTH : integer := 8
  );
  port (
    clk: in std_logic;
    reset: in std_logic;
    mosi: out std_logic;
    miso: in std_logic;
    cs: out std_logic;
    data_out: out std_logic_vector(DATA_WIDTH - 1 downto 0);
    data_in: in std_logic_vector(DATA_WIDTH - 1 downto 0);
    sck_delay: in integer := 0;
    cs_delay: in integer := 0
  );
end spi_master;

architecture behavioral of spi_master is
  signal sck: std_logic;
  signal bit_count: integer := 0;
  signal data: std_logic_vector(DATA_WIDTH - 1 downto 0) := (others => '0');
  signal cs_active: std_logic := '1';
  signal cs_delay_count: integer := 0;

begin

  process(clk, reset)
  begin
    if reset = '1' then
      sck <= '0';
      bit_count <= 0;
      cs_active <= '1';
      cs <= cs_active;
      cs_delay_count <= 0;
      data <= (others => '0');
    elsif rising_edge(clk) then
      if cs_delay_count < cs_delay then
        cs_delay_count <= cs_delay_count + 1;
        cs_active <= '1';
      else
        cs_active <= '0';
        cs <= cs_active;
        if sck_delay > 0 then
          wait for sck_delay * 1 ns;
        end if;
        if bit_count = 0 then
          mosi <= data(DATA_WIDTH - 1);
        else
          mosi <= data(bit_count - 1);
        end if;
        if bit_count < DATA_WIDTH then
          bit_count <= bit_count + 1;
          data_out <= (others => '0');
        else
          data_out <= data;
          bit_count <= 0;
        end if;
        sck <= not sck;
      end if;
    end if;
  end process;

  process(clk)
  begin
    if rising_edge(clk) then
      if cs_active = '0' then
        data(bit_count - 1) <= miso;
      end if;
    end if;
  end process;

end behavioral;

In this updated example, the generic DATA_WIDTH is used to specify the bit length of the SPI communication. The data_out and data_in signals are updated to use the DATA_WIDTH generic to ensure the correct bit length is used.

 

The rest of the design remains the same as the previous example, with the same processes being used to manage the SPI communication. The bit_count signal is updated to use the DATA_WIDTH generic to ensure that the counter matches the bit length of the communication.

頼み方をちょっと具体的にしたので、mosi, misoを多重化してしまった前回のミスは回避できていると思います。ではさらに、実行時可変仕様を追加します。

in your code, DATA_WIDTH is fixed. please change it a runtime configurable option.

--------------------------

Certainly, here's an updated version of the code with a runtime configurable DATA_WIDTH:

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

entity spi_master is
  port (
    clk: in std_logic;
    reset: in std_logic;
    mosi: out std_logic;
    miso: in std_logic;
    cs: out std_logic;
    data_out: out std_logic_vector(7 downto 0);
    data_in: in std_logic_vector(7 downto 0);
    data_width: in integer range 1 to 8 := 8;
    sck_delay: in integer := 0;
    cs_delay: in integer := 0
  );
end spi_master;

architecture behavioral of spi_master is
  signal sck: std_logic;
  signal bit_count: integer range 0 to 7 := 0;
  signal data: std_logic_vector(7 downto 0) := (others => '0');
  signal cs_active: std_logic := '1';
  signal cs_delay_count: integer range 0 to 255 := 0;

begin

  process(clk, reset)
  begin
    if reset = '1' then
      sck <= '0';
      bit_count <= 0;
      cs_active <= '1';
      cs <= cs_active;
      cs_delay_count <= 0;
      data <= (others => '0');
    elsif rising_edge(clk) then
      if cs_delay_count < cs_delay then
        cs_delay_count <= cs_delay_count + 1;
        cs_active <= '1';
      else
        cs_active <= '0';
        cs <= cs_active;
        if sck_delay > 0 then
          wait for sck_delay * 1 ns;
        end if;
        if bit_count = 0 then
          mosi <= data(data_width - 1);
        else
          mosi <= data(bit_count - 1);
        end if;
        if bit_count < data_width then
          bit_count <= bit_count + 1;
          data_out <= (others => '0');
        else
          data_out <= data;
          bit_count <= 0;
        end if;
        sck <= not sck;
      end if;
    end if;
  end process;

  process(clk)
  begin
    if rising_edge(clk) then
      if cs_active = '0' then
        data(bit_count - 1) <= miso;
      end if;
    end if;
  end process;

end behavioral;

In this updated code, DATA_WIDTH is replaced with a data_width input port, which allows the bit length to be set at runtime. The signal data and data_out are updated to match the width of the input data_width. The bit_count signal is also updated to match the range of the input data_width.

 

Note that the range of data_width is set from 1 to 8 to match the typical range of SPI communication. If a larger range is required, the range of bit_count and data would need to be updated accordingly.

1から8ビットまで設定できるって言ってきてます。最終的には1から33ビットが希望だけど、今回はこれで良しとしよう。では、テストベンチも生成してもらいます。

please generate a test bench vhdl code to test the spi implementation above.

--------------------------

Sure, here's a basic test bench for the SPI master implementation. This test bench provides a clock signal, a reset signal, and stimulus data to be transmitted over the SPI bus. It also receives data from the SPI bus and verifies that it matches the transmitted data.

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

entity spi_master_tb is
end spi_master_tb;

architecture behavioral of spi_master_tb is

  -- Component declaration for the SPI master
  component spi_master is
    port (
      clk: in std_logic;
      reset: in std_logic;
      mosi: out std_logic;
      miso: in std_logic;
      cs: out std_logic;
      data_out: out std_logic_vector;
      data_in: in std_logic_vector;
      data_width: in integer range 1 to 8 := 8;
      sck_delay: in integer := 0;
      cs_delay: in integer := 0
    );
  end component;

  -- Testbench signals
  signal clk: std_logic := '0';
  signal reset: std_logic := '0';
  signal mosi: std_logic;
  signal miso: std_logic;
  signal cs: std_logic;
  signal data_out: std_logic_vector(7 downto 0);
  signal data_in: std_logic_vector(7 downto 0);
  signal data_width: integer := 8;
  signal sck_delay: integer := 0;
  signal cs_delay: integer := 0;
  signal stimulus: std_logic_vector(7 downto 0) := "10101010";
  signal received_data: std_logic_vector(7 downto 0);

begin

  -- Instantiate the SPI master
  spi: spi_master
    port map (
      clk => clk,
      reset => reset,
      mosi => mosi,
      miso => miso,
      cs => cs,
      data_out => data_out,
      data_in => data_in,
      data_width => data_width,
      sck_delay => sck_delay,
      cs_delay => cs_delay
    );

  -- Stimulus process
  stimulus_proc: process
  begin
    -- Reset the SPI bus
    reset <= '1';
    wait for 10 ns;
    reset <= '0';
    wait for 10 ns;

    -- Transmit data over the SPI bus
    for i in 0 to 7 loop
      stimulus(i) <= '1';
      wait for 10 ns;
      stimulus(i) <= '0';
      wait for 10 ns;
    end loop;

    -- Wait for the last bit to be received
    wait for 10 ns;

    -- End simulation
    wait;
  end process;

  -- Clock generation process
  clk_proc: process
  begin
    while true loop
      clk <= not clk;
      wait for 5 ns;
    end loop;
  end process;

  -- Process to verify the received data matches the transmitted data
  verify_data_proc: process(data_out, data_in)
  begin
    for i in 0 to 7 loop
      if data_out(i) = '1' then
        assert data_in(i) = '1'
          report "Error: Received data does not match transmitted data"
          severity error;
      else
        assert data_in(i) = '0'
          report "Error: Received data does not match transmitted data"
          severity error;
      end if;
    end loop;
    wait;
  end process;

end behavioral;

In this test bench, the stimulus_proc process generates a series of 8-bit data transmissions to the SPI

まぁ、できた。では、前回と同じように、、、

はい。エラーです。センシティブプロセス内でwaitステートメントは使えません。そもそもテストベンチをなんでクロックセンシティブなプロセスで書く必要があったのか、、、自分の作ったコードの確認をするための最適で最小限の記述(一定時間待って、クロックエッジを待って、データをセットして、一定時間待つ、、、って感じ)ができそうなもんなのに、、、もうわけわからん。

 

ChatGPT、、、今のところ、確実な正解が求められる現場で使い物になるとは思えない。コンパイルすら通らないコードを持ってくるなんて。

 

やはり、人間は偉大だ。