FPGA UART Transmitter — Передатчик UART, реализованный на ПЛИС

В данной статье речь пойдет о передатчике UART, и она немного перекликается с предыдущей статьей [в ней Вы можете ознакомиться с теоретической частью работы UART и посмотреть код для приемопередатчика с тактовой частотой 20 Мгц и инф. скоростью 115200 кБит/с], за тем исключением, что здесь мы рассмотрим один из модулей Асинхронного Универсального Приемопередатчика (UART) — а именно передатчик.

Представленный ниже код является легко параметризуемым — его можно использовать в проектах с разными частотами и на разных информационных скоростях … следует лишь изменить один параметр.


Немного теории и о том, как использовать данный модуль передатчика …

В модуле используются следующие порты:

clk — входной однобитный порт. По данному порту подаются тактовые импульсы, по которым тактируется весь наш блок

data — входной 8-битный порт. На этом параллельном порту мы получаем данные, которые будем передавать по последовательному порту tx

data_rdy — входной однобитный порт. Лог. единица на данном порту сигнализирует о том, что данные (data) валидны и их необходимо передать по tx

tx — выходной однобитный порт. По нему мы передаем последовательный поток данных

transm_rdy — выходной однобитный порт. Данный порт сигнализирует остальным блокам о своем состоянии. Если на нем лог. 1, то блок — готов к передаче данных и ждет на вход data и data_rdy для передачи данных, если лог. 0 — то блок находится в процессе работы (занят)


Код передатчика — Verilog код

Здесь  сountOfStrobe — параметр, меняя который может регулировать информационную скорость нашего UART’а. В данном случае UART настроен на скорость 115200 кБит/с при тактовой частоте clk = 20 МГц.

сountOfStrobe = Fclk(Гц)/Vuart(бит/c) = 20*10^6 / 115200 = 173;

[php]
module uart_param_trans #(
                            parameter countOfStrobe = 173    // Параметр, который высчитывается из соотношения Fclk[Гц] / V[бит/c]
                                                            // соответствует тому, сколько тактов приходится на передачу одного
                                                            // бита данных при выбраной скорости UART(бит/с) и тактовой частоте CLK (Гц)
                            )(
                            input clk,                       // Тактовая частота
                            input [7:0] data,              // Данные, которые собираемся передать по TX
                            input data_rdy,                // Строб, который соответствует тому, что данные валидные и их нужно передать
                            output reg tx = 1,          // Выходной порт, передает данные по последовательному интерфейсу
                            output reg transm_rdy = 1   // Строб, который сигнализирует о том, что данные переданы и блок готов к передаче новых данных
                            );
    
    reg [1:0] state = 2’b00;     // Регистр, который будет менять значение в зависимости от состояния нашего модуля
    reg [7:0] cntStrobe = 0;     // Регистр — счетчик, который будет накапливаться до необходимого числа стробов (до countOfStrobe)
    reg [4:0] cntBit = 0;         // Регистр — счетчик, указываюший на номер передаваемого бита из data
    reg [7:0] shiftData;         // Сдвиговый регистр, в который мы записываем входные данные (data), а затем последовательно, побитово
                                // передаем по TX
    
    always @(posedge clk)
    begin
        case(state)
            2’b00 :    begin
                        if (data_rdy)
                        begin
                            state <= 2’b01;
                            shiftData <= data;
                            transm_rdy <= 0;         
                            tx <= 0;
                        end
                    end
            2’b01 :    begin
                        if (cntBit == 0)
                        begin
                            if (cntStrobe < countOfStrobe)
                                cntStrobe <= cntStrobe + 1;
                            else
                            begin
                                cntStrobe <= 0;
                                cntBit <= 1;
                                tx <= shiftData[0];
                                shiftData[6:0] <= shiftData[7:1];
                            end
                        end
                        if (cntBit > 0 && cntBit < 9)
                        begin
                            if (cntStrobe < countOfStrobe)
                                cntStrobe <= cntStrobe + 1;
                            else
                            begin
                                cntStrobe <= 0;
                                cntBit <= cntBit + 1;
                                tx <= shiftData[0];
                                shiftData[6:0] <= shiftData[7:1];
                            end
                        end
                        if (cntBit == 9)
                        begin
                            if (cntStrobe < countOfStrobe)
                            begin
                                cntStrobe <= cntStrobe + 1;                             
                                tx <= 1;
                            end
                            else
                            begin
                                cntStrobe <= 0;
                                cntBit <= 0;
                                transm_rdy <= 1;
                                state <= 2’b00;
                            end
                        end
                    end
            
        endcase
    end
endmodule
[/php]


Testbench проекта … Симуляция работы модуля в ModelSim

Для того, чтобы убедиться в том, что модуль работает — напишем тестбенч. Тестбенч — файл, который пишется для симуляции работы модуля и он симулирует работу портов, так, как это указано в техническом задании.

В нашем случае testbench подаст на наш блок — тактовый сигнал clk, данные data и строб data_rdy, при этом он будет следить за стробом transm_rdy и когда тот будет переходить в лог.1 (т.е. передатчик будет готов передавать новые данные по tx) testbench будет инкрементировать данные  (увеличивать значение на 1) и переводить data_rdy в лог. 1.

[php]
`timescale 1ns / 100 ps;
    module uart_param_tb();
    parameter fr = 50; // 20 MHz
    
    reg clk = 0;
    reg [7:0] data = 8’b0111_1110;
    reg data_rdy = 0;
    reg [1:0] state = 2’b00;
    
    wire tx, transm_rdy;
    
    always
    begin
        #(fr/2) clk = 1;
        #(fr/2) clk = 0;
    end
    
    always @(posedge clk)
    begin
    
    case (state)
        2’b00 :    begin
                    if (transm_rdy)
                    begin
                        state <= 2’b01;
                        data <= data + 1;
                    end
                end
        2’b01 : begin
                    data_rdy <= 1;
                    state <= 2’b10;
                end
        2’b10 : begin
                    data_rdy <= 0;
                    state <= 2’b00;
                end
    endcase
    end
    
    
    uart_param_trans tb(
                    .clk(clk),                       
                    .data(data),              
                    .data_rdy(data_rdy),                
                    .tx(tx),          
                    .transm_rdy(transm_rdy)   
                    );
    
endmodule
[/php]


Результаты моделирования в ModelSim

Ниже представлены рисунки симуляции при скорости UART 115200 и 230400 Кбит/c. Причем для изменения скорости, как говорилось выше, был поменян только один параметр.

87

Работа модуля на скорости 230400 Кбит/с

173

Работа модуля на скорости 115200 Кбит/с

Из рисунков видно, что передатчик работает правильно.


Файлы для скачивания

Вы можете скачать файлы ( uart_trans ). В архиве прикреплен файл script.do, в котором указаны все параметры для запуска симуляции в ModelSim. Для его запуска необходимо разархивировать архив, в ModelSim с помощью команды

[php]

cd вашаДиректорияГдеЛежатФайлы (Например C:/Users/Me/Decktop/thisPrj/)

do script.do

[/php]

Если есть вопросы, замечания, предложения — пиши в комментариях

Добавить комментарий