Simtera. Соединяя Миры. Работа с UART
Добрый день!
Продолжая серию рассказов о HDL-симуляторе, я, как обещал в прошлый раз, расскажу о создании мультиязычного проекта (Verilog/VHDL). В качестве демонстрации покажу как реализована работа UART в нашем симуляторе. То есть на одном примере мы увидим сразу несколько достоинств Simter'ы.
Итак...
UART - это асинхронный последовательный приемо-передатчик. Он является довольно распространенным способом обмена данными между различными устройствами. Также как и Simtera, он соединяет различные "миры". Разница лишь в том, что наш симулятор объединяет в себе разные миры двух, непохожих друг на друга, языков описания аппаратуры - VHDL и Verilog.
Моделируемое устройство состоит из топового (головного) модуля TestBench, передатчика UART Transmitter и приемника UART Receiver. Головной модуль передает сигнал на передатчик. Приемник принимает сигнал и передает его обратно в головной модуль. Далее в головном модуле происходит сравнения отправленного и полученного пакетов данных. Если проверка прошла успешно, то головной модуль отправляет на передатчик следующий пакет.
Приемник релизован на Verilog, передатчик - на VHDL. Топовый модуль также сконструирован на Verilog. Код, описывающий работу модулей я поместил в конце поста.
Структура представлена на рисунке, толстыми линии обозначают 8-битные шины передачи данных, тонкие - однобитные.
Характеристики устройства:
- Тактовая частота схемы - 20 МГц
- Скорость UART: 115200 бод
- Используются только стартовый и стоповый биты
Назначение шин:
- data8_i - 8-битная шина передачи данных на UART Transmitter
- data8_o - 8-битная шина с получения данных, принятых UART Receiver'ом
- rdytr - сигнал о готовности работы UART Transmitter
- rdy_i - сигнал для UART Transmitter о старте передачи данных с шины data8_i
- rdy - сигнал для TestBench для чтения данных с шины data8_o
- error - сигнал для TestBench об ошибке в данных, передаваемых по шине data8_o
- clk - тактовый сигнал
Следующая gif'ка- демонстрирует работу устройста, промоделированную с помощью Simter'ы. Отдельно отмечу плавность работы осциллографа. При отрисовке сигналов и их масштабировании (по мере поступления новых даных) изображение изменяется постепенно, без всяких "дерганий", в отличии от, например, ModelSim. Другим преймуществом является скорость отображения данных - изменения происходят в реальном времени.
В каждой статье про Simter'у я постараюсь максимально подробно рассказывать о работе с системой и приводить исходный код демонстрационных примеров. Данная статья не является исключением - ниже код для каждого из модулей. Буду рад ответить на ваши вопросы.
module receiver ( input clk, input rx, output reg [7:0] data8_o, output reg rdy = 1, output reg error ); parameter IDLE = 2'b00, RECEIVE = 2'b01, OUT_DATA = 2'b10; reg [1:0] state = IDLE; reg [8:0] cnt_beats = 0; reg [7:0] data; reg [3:0] cnt_bits = 0; reg [2:0] chosenData = 0; reg [2:0] chCnt = 0; always @(posedge clk) begin case (state) 2'b00 : begin if (!rx) begin state <= RECEIVE; rdy <= 0; end end 2'b01 : begin if (cnt_bits < 9) begin if (cnt_beats < 173) begin cnt_beats <= cnt_beats + 1; if (cnt_beats == 50 | cnt_beats == 100 | cnt_beats == 150) begin chosenData[chCnt] = rx; chCnt <= chCnt + 1; end end else begin chCnt <= 0; cnt_beats <= 0; cnt_bits <= cnt_bits + 1; if (cnt_bits >= 1) data[cnt_bits - 1] <= chosenData[1]; error <= (chosenData[0] ^ chosenData[1]) ^ (chosenData[1] ^ chosenData[2]); end end else begin cnt_bits <= 0; state <= OUT_DATA; end end 2'b10 : begin state <= IDLE; rdy <= 1; data8_o <= data; end endcase end endmodule |
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity transmitter is port( clk : in std_logic; data8_i : in unsigned(7 downto 0); rdy : in std_logic; tx : out std_logic := '1'; rdytr : out std_logic := '1' ); end entity; architecture transmitter of transmitter is signal stateTrans : unsigned(1 downto 0) := "00"; signal data2sent : unsigned (7 downto 0) := "00000000"; signal cnt_beatsTr : unsigned(7 downto 0) := to_unsigned(0,8); signal cnt_bitsTr : unsigned(7 downto 0) := to_unsigned(0,8); signal FR : unsigned(7 downto 0) := to_unsigned(173,8); begin process(clk) begin if (rising_edge(clk)) then case stateTrans is when "00" => if (rdy = '1') then stateTrans <= "01"; data2sent <= data8_i; rdyTr <= '0'; end if; when "01" => -- Sending start bit if (cnt_beatsTr < FR) then cnt_beatsTr <= cnt_beatsTr + to_unsigned(1,8); tx <= '0'; else stateTrans <= "10"; cnt_beatsTr <= to_unsigned(0,8); end if; when "10" => if (cnt_bitsTr < to_unsigned(8,8)) then if (cnt_beatsTr < FR) then cnt_beatsTr <= cnt_beatsTr + to_unsigned(1,8); tx <= data2sent(to_integer(cnt_bitsTr)); else cnt_beatsTr <= to_unsigned(0,8); cnt_bitsTr <= cnt_bitsTr + to_unsigned(1,8); end if; else stateTrans <= "11"; cnt_beatsTr <= to_unsigned(0,8); cnt_bitsTr <= to_unsigned(0,8); end if; when "11" => -- Sending stop bit if (cnt_beatsTr < FR) then cnt_beatsTr <= cnt_beatsTr + to_unsigned(1,8); tx <= '1'; else cnt_beatsTr <= to_unsigned(0,8); rdyTr <= '1'; stateTrans <= "00"; end if; when others => stateTrans <= "00"; end case; end if; end process; end architecture; |
module top; reg clk = 1; reg rdy = 1; wire txrx; wire [7:0] dataFromReceiver; wire receiverRdy; wire transmitterrdy; wire receiverError; reg errorFlag = 0; reg [7:0] data2transmitter = 8'd121; always #25000000 clk <= ~clk; always @(posedge clk) begin if (transmitterrdy) begin if (dataFromReceiver == data2transmitter) data2transmitter <= data2transmitter + 11; rdy <= 1; errorFlag <= 0; end else begin rdy <= 0; errorFlag <= 1; end end receiver receiverTb ( .clk(clk), .rx(txrx), .data8_o(dataFromReceiver), .rdy(receiverRdy), .error(receiverError) ); transmitter transmitterTb ( .clk(clk), .data8_i(data2transmitter), .rdy(rdy), .tx(txrx), .rdytr(transmitterrdy) ); endmodule |
После того, как мы просимулировали проект - синтезируем его и "прошьем" в ПЛИС (FPGA). Для этого воспользуемся отладочной платой (development kit) от Microsemi.
Данные отправляются с ПК, ПЛИС принимает их, обрабатывает и передает обратно.
Схема проекта в Microsemi Libero SoC представлена на рисунке ниже.
После того, как чип "прошит", попробуем пообщаться с ним. Для этого воспользуемся консолью COM - порта, например, Putty, настроенному на нашу скорость UART (115200 бод). Для визуализации работы, transmitter постоянно передает данные, принятые receiver'ом.
С уважением, Никита Малышев.
Другие записи
-
Читать далее015927.07.2022
-
Новое видео на нашем канале Youtube
Читать далее023811.07.2022