Перейти к содержанию

    

Вопрос по передаче параметра в процедуру на VHDL

Здравствуйте все!

Я пишу тестбенч для модуля I2C на VHDL. Делаю процедуру для передачи байта, сам байт передаю как параметр в эту процедуру.

Старший бит каждого байта формируется неправильно, остальные - правильно. Картинку прикладываю.

Вот код. В нём одна строка

wait for 1 ns;

закомментирована. Если её раскомментировать, то получается то, что мне надо. Можно бы и так оставить, но некрасиво.

Подскажите, пожалуйста, какую тонкость VHDL я не понимаю?

Заранее признателен.

library ieee;
use ieee.std_logic_1164.all;

entity myI2C_proc_tb is
end myI2C_proc_tb;

architecture behavioral of myI2C_proc_tb is

 constant clk_period : time := 44 ns;
 signal clk: std_logic := '0';  --тактовая частота модуля I2C_proc
 signal Reset: std_logic := '0';
 signal SCL: std_logic := '1';
 signal SDA: std_logic := '1';
 constant SCL_period: time := 4.35 us;  --период SCLK
 constant tri: time := SCL_period / 3.0;  --треть периода SCK, время, за которое ничего не происходит
 signal MyByte: std_logic_vector(7 downto 0);

--Объявления процедур
 procedure i2c_start (signal mySCL: out std_logic; signal mySDA: out std_logic) is
   begin
     mySDA <= '1';
     mySCL <= '1';
     wait for tri;
     mySDA <= '0';
     wait for tri;
     mySCL <= '0';
     wait for tri;
   end procedure i2c_start;

 procedure i2c_stop (signal mySCL: out std_logic; signal mySDA: out std_logic) is
   begin
     mySDA <= '0';
     mySCL <= '0';
     wait for tri;
     mySCL <= '1';
     wait for tri;
     mySDA <= '1';
     wait for tri;
   end procedure i2c_stop;

 procedure i2c_writebyte (signal ySCL: out std_logic; signal ySDA: out std_logic;
                          signal ByteValue: in std_logic_vector(7 downto 0)) is
     variable i: natural;
   begin
     --Передача байта:
     i := 8;
--      wait for 1 ns;
     while i > 0
       loop
         ySDA <= ByteValue(i - 1);
         wait for tri;
         ySCL <= '1';
         wait for tri;
         ySCL <= '0';
         wait for tri;
         i := i - 1;
       end loop;
     --Ожидание подтверждения:
     ySDA <= 'Z';
     wait for tri;
     ySCL <= '1';
     wait for tri;
     ySCL <= '0';
     wait for tri;
   end procedure i2c_writebyte;

begin

--Тактовая частота:
 clk <= not clk after (clk_period / 2.0 );

--Собственно моделирование
model_l:
 process

 begin
   --Сброс схемы:
   wait for clk_period * 2;
   Reset <= '0';
   --Посылка:
   i2c_start(SCL, SDA);
   MyByte(7 downto 1) <= B"0100001";
   MyByte(0) <= '0';
   i2c_writebyte(ySCL => SCL, ySDA => SDA, ByteValue => MyByte);  --адресный байт
   MyByte <= X"AA";
   i2c_writebyte(SCL, SDA, MyByte);
   MyByte <= X"AA";
   i2c_writebyte(SCL, SDA, MyByte);
   MyByte <= X"55";
   i2c_writebyte(SCL, SDA, MyByte);
   i2c_stop(SCL, SDA);

   wait; 
 end process model_l;
end behavioral;

post-1486-1535371112_thumb.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я когда писал различные процедуры для тестирования I2С сделал чуть по-другому.

Я период клока SCL разделил на 4 части (удобно имитировать старт и стоп биты).

Ещё очень удобно имитировать подтяжку линий SCL и SDA при помощи сигнала типа "H" - благо VHDL умеет это.

И простой функцией мы преобразуем лог.1 в подтяжку:

    -----------------------------------------------------------------------------------------------
    -- функция преборазования лог 1 в подтяжку
    function one_to_h (input : std_logic)     -- входные данные это обычный единичный бид
        return std_logic is                   -- возвращает такой-же бит
    begin
        if (input = '1') then                 -- если оказалось, что входной бит это 'h' состояние
            return 'h';                       -- вместо единички делаем подтяжку
        else                                  -- иначе
            return '0';                       -- возвращаем ноль
        end if;
    end one_to_h;

 

Вот так у меня выглядела процедура передачи байта от Master -> Slave:

 

     procedure i2c_data_to_slave 
        (
            signal slave_data_sig : in std_logic_vector(7 downto 0)
        )
        is
        begin
            for i in 7 downto 0 loop
                SDA <= one_to_h(slave_data_sig(i));
                SCL <= '0';
                wait for SCL_prd/4;            
                SDA <= one_to_h(slave_data_sig(i));
                SCL <= 'h';
                wait for SCL_prd/4;
                SDA <= one_to_h(slave_data_sig(i));
                SCL <= 'h';
                wait for SCL_prd/4;
                SDA <= one_to_h(slave_data_sig(i));
                SCL <= '0';
                wait for SCL_prd/4;
                SDA <= 'h';
                SCL <= 'h';
            end loop;
        end procedure i2c_data_to_slave;

 

Вот так вот у меня выглядит запись в Slave данных от 255 до 0 побайтно:

   --=============================================================================
        -- рандомное чтение каждой ячекйи 
        --=============================================================================
        wait for SCL_prd*10;
        testing  <= random_write;                                               -- включим состояние тестирования записи данных в память
        report lf & lf &"============== random write =================" & lf;  -- напишем что именно тестируем
        for i in 255 downto 0 loop
            i2c_start;                                                         -- старт условие
            i2c_addr_slave(slave_addr);                                        -- запрос адреса slave устройства
            direction_data(write_ram);                                         -- запрос на чтение
            i2c_slave_ack;                                                     -- подтверждение адреса слейва
            ram_addr_zero <= std_logic_vector(to_unsigned(i,8));
            i2c_data_to_slave(ram_addr_zero);                                  -- отправляем адрес ячейки с которой хотим работать (ячейка № 3)
            i2c_slave_ack;                                                     -- slave говорит ack придавив линию к земле   
            i2c_data_to_slave(ram_addr_zero);                                  -- записываем данные в память
            i2c_slave_ack;                                                     -- master говорит ack придавив линию к земле
            i2c_stop;                                                          -- стоп условие   
        end loop;

 

Если хотите могу поделиться тем, что у меня было сделано для тестирования I2C

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В качестве параметров вы используете сигналы, в которые перед вызовом процедуры выполнено отложенное присваивание. Первый по времени(старший) бит уходит ещё до того, как теневое значение сигнала синхронизировалось с видимым. Это можно пофиксить с помощью wait for 0s, тогда на временной диаграмме не будет видимых отличий, а лучше стараться вообще не использовать сигналы в тестбенчах без особой надобности, а если использовать, то без отложенных присваиваний.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
В качестве параметров вы используете сигналы, в которые перед вызовом процедуры выполнено отложенное присваивание. Первый по времени(старший) бит уходит ещё до того, как теневое значение сигнала синхронизировалось с видимым. Это можно пофиксить с помощью wait for 0s, тогда на временной диаграмме не будет видимых отличий, а лучше стараться вообще не использовать сигналы в тестбенчах без особой надобности, а если использовать, то без отложенных присваиваний.

Спасибо, кажется, дошло. Сигнал может принять новое значение или после завершения всех происшествий, вызванных последним событием, или после оператора wait. У меня в месте вызова процедуры было присвоение значения байту, потом запуск процедуры, потом присвоение значения биту, и всё это время у сигнала нет повода завершить присвоения. То есть, реальное присвоение значения отправляемому байту произойдёт только после первого wait. Так?

wait for 0 s помогло.

А почему нельзя использовать сигналы в тестбенче? Как бы я мог иначе передавать в I2C разные байты через такую процедуру?

 

to Flip-fl0p

Спасибо, я тоже с удовольствием использую 'H'. Только ничего не преобразую, а в тексте описания модуля проекта пишу что-то вроде

if ipSCL = '0' then SCL <= '0'; else SCL <= '1'; end if; --Если ipSCL = 'H', то SCL <= 1

, где ipSCL - сигнал на выводе ПЛИС, а SCL - сигнал внутри неё, с которым работает начинка проекта.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Спасибо, кажется, дошло. Сигнал может принять новое значение или после завершения всех происшествий, вызванных последним событием, или после оператора wait. У меня в месте вызова процедуры было присвоение значения байту, потом запуск процедуры, потом присвоение значения биту, и всё это время у сигнала нет повода завершить присвоения. То есть, реальное присвоение значения отправляемому байту произойдёт только после первого wait. Так?
Всё так.
wait for 0 s помогло.

А почему нельзя использовать сигналы в тестбенче? Как бы я мог иначе передавать в I2C разные байты через такую процедуру?

Сигналы можно использовать, но только там, где нужно. В данном случае, например, для SCL и SDA. А вот MyByte должен быть переменной. Кстати, переменные занимают меньше памяти и быстрее симулируются.

 

Я тоже иногда использовал wait for 0, не всё можно удобно сделать на переменных.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Всё так.

Сигналы можно использовать, но только там, где нужно. В данном случае, например, для SCL и SDA. А вот MyByte должен быть переменной. Кстати, переменные занимают меньше памяти и быстрее симулируются.

Я тоже иногда использовал wait for 0, не всё можно удобно сделать на переменных.

Большое спасибо. Я сделал MyByte переменной, теперь не надо wait for 0, чувствую себя культурным человеком :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти