Приложение 1. Создание проекта в Xilinx Platform Studio
Отчет
1. Структурная схема IP ядра UART
Рис. 1.1 Структурная схема IP ядра UART
2. Описание работы модуля
Назначение. IP ядро UART предназначено для организации внешнего интерфейса обмена данными между внешним цифровым устройством и софт-процессором Microblaze по универсальной шине AXI с поддержкой интерфейса AXI4-Lite.
Характеристики:
- данные передаются последовательно по одному байту в сопровождении так называемых «стартового» и «стопового» битов (см. рис. 2.1) по двум независимым линиям rxd и txd приемника и передатчика модуля соответственно; в состоянии ожидания на линиях rxd и txd установлен логический уровень «0» ;
Рис. 2.1 Временная диаграмма обмена данными между внешним цифровым устройством и модулем интерфейса UART по независимым линиям rxd и txd
- поддерживаются следующие скорости обмена данными межу софт-процессором Microblaze и внешним цифровым устройством:
ü 9600 бод/сек;
ü 19200 бод/сек;
ü 115200 бод/сек;
- поддержка универсального интерфейса AXI4-Lite;
- IP ядро UART – slave-устройство на шине AXI.
Сигналы модуля и их назначение
Табл. 1 Сигналы IP ядра UART и их назначение
Имя сигнала | Направление сигнала | Разрядность | Описание |
S_AXI_ACLK | input | Глобальная тактовая частота, по переднему фронту S_AXI_ACLK синхронизируются все сигналы | |
S_AXI_ARSTN | input | Глобальный сигнал сброса. Активный уровень – логический «0» | |
S_AXI_AWADDR | input | Адрес (Memory Mapped) для записи данных в модуль IP ядра UART со стороны Master-устройства на шине AXI (процессора Microblaze) | |
S_AXI_AWVALID | input | Сигнал подтверждения адреса S_AXI_AWADDR | |
S_AXI_WDATA | input | Данные для записи в модуль IP ядра UART, выставленные Master-устройством на шине AXI | |
S_AXI_WSTRB | input | Строб записи данных | |
S_AXI_WVALID | input | Сигнал подтверждения данных для записи S_AXI_WDATA и строба записи данных S_AXI_WSTRB на шине | |
S_AXI_BREADY | input | Сигнал готовности Master-устройства на шине AXI принимать ответную информацию | |
S_AXI_ARADDR | input | Адрес (Memory Mapped) для чтения данных из модуля IP ядра UART со стороны Master-устройства на шине AXI (процессора Microblaze) | |
S_AXI_ARVALID | input | Сигнал подтверждения адреса S_AXI_ARADDR | |
S_AXI_RREADY | input | Сигнал готовности чтения данных или ответной информации Master-устройством на шине AXI | |
S_AXI_ARREADY | output | Сигнал готовности адреса чтения. Указывает на то, что устройство модуля IP ядра UART (slave-устройство на шине AXI) готово принять адрес для чтения и сопровождающие управляющие сигналы | |
S_AXI_RDATA | output | Данные для чтения из модуля IP ядра UART Master-устройством на шине AXI | |
S_AXI_RRESP | output | Сигнал статуса операций чтения (известно 4 состояния сигнала S_AXI_RRESP: OKAY, EXOKAY, SLVERR, DECERR) | |
S_AXI_RVALID | output | Сигнал подтверждения данных для чтения S_AXI_RDATA на шине AXI Master-устройством | |
S_AXI_WREADY | output | Сигнал готовности устройства модуля IP ядра UART принять данные S_AXI_WDATA от Master-устройства | |
S_AXI_BRESP | output | Сигнал статуса операций записи (известно 4 состояния сигнала S_AXI_RRESP: OKAY, EXOKAY, SLVERR, DECERR) | |
S_AXI_BVALID | output | Сигнал подтверждения статуса операций записи S_AXI_BRESP | |
S_AXI_AWREADY | output | Сигнал готовности адреса записи. Указывает на то, что устройство модуля IP ядра UART (slave-устройство на шине AXI) готово принять адрес для записи и сопровождающие управляющие сигналы | |
rxd | input | Вход интерфейса UART | |
txd | output | Выход интерфейса UART |
Настройка IP ядра UART и организация обмена данными между процессором Microblaze и внешним цифровым устройством, используя интерфейс UART
1) Необходимо в сгенерированном мастером (пошаговая инструкция создания необходимых проектов в Приложении 1) HDL-описании user_logic.v подключить модуль uart_top, указав необходимые параметры:
ü FREQ – тактовая частота в МГц;
ü RATE – скорость передачи данных по интерфейсу UART (RATE = 1 для скорости 9600 бод/сек, RATE = 2 для скорости 19200 бод/сек, RATE = 12 для скорости 115200 бод/сек);
2) Из заготовленных мастером регистров (slv_reg0, slv_reg1) выбрать регистр управления (назначить ему все сигналы управления модулем uart_top.v и данные для передачи со стороны Master-устройства) и регистр состояний (назначить ему все сигналы состояний модуля uart_top.v и данные для чтения Master-устройством);
3) Вывести внешние сигналы интерфейса UART rxd и txd в главный модуль проекта, сгенерированного мастером
4) Добавить разработанное IP-ядро в систему с процессором Microblaze (Xilinx XPS Studio -> Create or Import Peripheral…, следовать указаниям мастера)
5) Назначить внешние сигналы IP UART rxd и txd (Xilinx XPS Studio -> System Assembly View -> Ports -> <uart_ip_0> -> ПКМ -> Make External)
6) Проверить на наличие ошибок (Generate Netlist)
Система на базе процессора Microblaze с подключенным к ней разработанным IP ядром UART готова к работе.
3. Временные диаграммы
Рис 3.1 Временная диаграмма обмена данными между процессором Microblaze и внешним цифровым устройством с использованием разработанного IP ядра UART
В процессе моделирования был рассмотрен следующий пример: процессор по шине AXI выставляет данные в адресуемый регистр управления slv_reg1(данные для передачи – 8 младших разрядов регистра, сигнал начала передачи – 8-й разряд регистра) для передачи внешнему устройству с помощью IP ядра UART. В файле тестовых воздействий выходной сигнал системы uart_ip_0_txd_pin (выход интерфейса UART) замкнут на входной uart_ip_0_rxd_pin (вход интерфейса UART).
В результате отрабатывается передача данных по линии uart_ip_0_txd_pin и прием по линии uart_ip_0_rxd_pin с задержкой в 1 такт системной частоты. Принятые данные записываются в младшие разряды адресуемого со стороны процессора регистра состояний slv_reg0, что и отображено на временной диаграмме.
4. Исходный код разработанного модуля (описание на языке VerilogHDL)
1) Код верхнего уровня модуля интерфейса UART (файл uart_top.v)
// bit rate: 9600 kbit/sec
// clock : 100 MHz
module uart_top #(parameter FREQ = 100,
parameter RATE = 1,
parameter MAJ_DIV = 128)
(
input clock,
input reset_n,
input data_tx,
output data_recieved,
input trans_en,
output rec_succ,
output error,
output uart_busy,
input rxd,
output txd
);
wire clock;
wire reset_n;
wire [7:0] data_tx;
reg [7:0] data_recieved;
wire trans_en;
reg rec_succ;
reg error;
reg uart_busy;
wire rxd;
wire txd;
// LOCAL PARAMETERS
// FREQ is in MHz
// if FREQ = 100, real frequency is 100 MHz (100 000 000 Hz)
// real rate = 9600*RATE;
// if RATE = 2, real rate is 19200 bod/sec
localparam CNT_PULSES = (FREQ*1000000)/(9600*RATE);
// shift reg for TX
reg [9:0] tx_reg;
reg [13:0] tx_cnt;
reg [1:0] state_tx;
// shift reg for RX
reg [9:0] rx_reg;
reg [13:0] rx_cnt;
reg [1:0] state_rx;
reg [3:0] tx_cnt_bits;
reg [3:0] rx_cnt_bits;
reg rx_maj;
assign txd = tx_reg[0];
majority_scheme #(CNT_PULSES, MAJ_DIV)
majority_scheme_inst(
.clock(clock),
.reset_n(reset_n),
.rx_maj(rx_maj),
.rx_cnt(rx_cnt),
.maj_out_bit(maj_out_bit)
);
always @(posedge clock or negedge reset_n)
if (~reset_n)
begin
tx_reg <= 0;
rx_reg <= 0;
rx_cnt <= 0;
tx_cnt <= 0;
state_tx <= 0;
state_rx <= 0;
uart_busy <= 0;
data_recieved <= 0;
rec_succ <= 0;
rx_maj <= 0;
tx_cnt_bits <= 0;
rx_cnt_bits <= 0;
error <= 0;
end
else
begin
// One pulse
rec_succ <= 0;
// Transmitter
case (state_tx)
// idle
0: begin
if (trans_en)
state_tx <= 1;
end
// register data to transmit
1: begin
uart_busy <= 1;
tx_reg[8:1] <= data_tx;
tx_reg[9] <= 0; // stop bit
tx_reg[0] <= 1; // start bit
state_tx <= 2;
end
// transmit 10 bits
2: begin
if (tx_cnt == 10417)
begin
tx_cnt <= 0;
tx_reg[8:0] <= tx_reg[9:1];
tx_reg[9] <= 0;
tx_cnt_bits <= tx_cnt_bits + 1;
if (tx_cnt_bits == 9)
begin
state_tx <= 0;
uart_busy <= 0;
tx_cnt_bits <= 0;
end
end
else
tx_cnt <= tx_cnt + 1;
end
endcase
// Reciever
case (state_rx)
// idle
0: begin
if (rxd)
state_rx <= 1;
end
// recieve 10 bits
1: begin
rx_maj <= rxd;
if (rx_cnt_bits == 10)
begin
state_rx <= 2;
rx_cnt_bits <= 0;
end
if (rx_cnt == CNT_PULSES)
begin
rx_cnt <= 0;
rx_reg[9] <= maj_out_bit;
rx_reg[8:0] <= rx_reg[9:1];
rx_cnt_bits <= rx_cnt_bits + 1;
end
else
rx_cnt <= rx_cnt + 1;
end
// register recieved data
2: begin
data_recieved <= rx_reg[8:1];
if (~rx_reg[9])
rec_succ <= 1;
else
error <= 1;
state_rx <= 0;
end
endcase
end
endmodule
2) Код мажоритарной схемы приемника модуля UART (файл majority_scheme.v)
module majority_scheme #(parameter CNT_PULSES = 1,
parameter MAJ_DIV = 2)
(
// System
input wire clock,
input wire reset_n,
input wire [13:0] rx_cnt,
input wire rx_maj,
output reg maj_out_bit
);
localparam MIN_CNT_ONES = CNT_PULSES/(64*MAJ_DIV);
reg [2:0] cnt_ones;
always @(posedge clock or negedge reset_n)
if (~reset_n)
begin
maj_out_bit <= 0;
cnt_ones <= 0;
end
else
begin
if (rx_cnt[MAJ_DIV + 5:0] == 7'b0000000)
begin
if (rx_maj)
cnt_ones <= cnt_ones + 1;
else
cnt_ones <= cnt_ones + 0;
end
if (cnt_ones > MIN_CNT_ONES && rx_cnt == CNT_PULSES - 1)
begin
maj_out_bit <= 1;
cnt_ones <= 0;
end
else if (cnt_ones <= MIN_CNT_ONES && rx_cnt == CNT_PULSES - 1)
begin
maj_out_bit <= 0;
cnt_ones <= 0;
end
end
endmodule
3) Код модуля user_logic.v
`uselib lib=unisims_ver
`uselib lib=proc_common_v3_00_a
module user_logic
(
// -- ADD USER PORTS BELOW THIS LINE ---------------
// --USER ports added here
// -- ADD USER PORTS ABOVE THIS LINE ---------------
txd,
rxd,
// -- DO NOT EDIT BELOW THIS LINE ------------------
// -- Bus protocol ports, do not add to or delete
Bus2IP_Clk, // Bus to IP clock
Bus2IP_Resetn, // Bus to IP reset
Bus2IP_Data, // Bus to IP data bus
Bus2IP_BE, // Bus to IP byte enables
Bus2IP_RdCE, // Bus to IP read chip enable
Bus2IP_WrCE, // Bus to IP write chip enable
IP2Bus_Data, // IP to Bus data bus
IP2Bus_RdAck, // IP to Bus read transfer acknowledgement
IP2Bus_WrAck, // IP to Bus write transfer acknowledgement
IP2Bus_Error // IP to Bus error response
// -- DO NOT EDIT ABOVE THIS LINE ------------------
); // user_logic
// -- ADD USER PARAMETERS BELOW THIS LINE ------------
// --USER parameters added here
// -- ADD USER PARAMETERS ABOVE THIS LINE ------------
// -- DO NOT EDIT BELOW THIS LINE --------------------
// -- Bus protocol parameters, do not add to or delete
parameter C_NUM_REG = 2;
parameter C_SLV_DWIDTH = 32;
// -- DO NOT EDIT ABOVE THIS LINE --------------------
// -- ADD USER PORTS BELOW THIS LINE -----------------
// --USER ports added here
// -- ADD USER PORTS ABOVE THIS LINE -----------------
input rxd;
output txd;
// -- DO NOT EDIT BELOW THIS LINE --------------------
// -- Bus protocol ports, do not add to or delete
input Bus2IP_Clk;
input Bus2IP_Resetn;
input [C_SLV_DWIDTH-1 : 0] Bus2IP_Data;
input [C_SLV_DWIDTH/8-1 : 0] Bus2IP_BE;
input [C_NUM_REG-1 : 0] Bus2IP_RdCE;
input [C_NUM_REG-1 : 0] Bus2IP_WrCE;
output [C_SLV_DWIDTH-1 : 0] IP2Bus_Data;
output IP2Bus_RdAck;
output IP2Bus_WrAck;
output IP2Bus_Error;
// -- DO NOT EDIT ABOVE THIS LINE --------------------
//----------------------------------------------------------------------------
// Implementation
//----------------------------------------------------------------------------
// --USER nets declarations added here, as needed for user logic
// Nets for user logic slave model s/w accessible register example
reg [C_SLV_DWIDTH-1 : 0] slv_reg0;
reg [C_SLV_DWIDTH-1 : 0] slv_reg1;
wire [1 : 0] slv_reg_write_sel;
wire [1 : 0] slv_reg_read_sel;
reg [C_SLV_DWIDTH-1 : 0] slv_ip2bus_data;
wire slv_read_ack;
wire slv_write_ack;
integer byte_index, bit_index;
// USER logic implementation added here
// user lines
wire [7:0] data_tx ;
wire trans_en;
wire [7:0] data_recieved;
wire rec_succ;
wire uart_busy;
wire error;
wire txd;
wire rxd;
assign data_tx = slv_reg1[7:0];
assign trans_en = slv_reg1[8];
uart_top uart_top_inst(
.clock(Bus2IP_Clk),
.reset_n(Bus2IP_Resetn),
.data_tx(data_tx),
.data_recieved(data_recieved),
.trans_en(trans_en),
.rec_succ(rec_succ),
.error(error),
.uart_busy(uart_busy),
.rxd(rxd),
.txd(txd)
);
// ------------------------------------------------------
// Example code to read/write user logic slave model s/w accessible registers
//
// Note:
// The example code presented here is to show you one way of reading/writing
// software accessible registers implemented in the user logic slave model.
// Each bit of the Bus2IP_WrCE/Bus2IP_RdCE signals is configured to correspond
// to one software accessible register by the top level template. For example,
// if you have four 32 bit software accessible registers in the user logic,
// you are basically operating on the following memory mapped registers:
//
// Bus2IP_WrCE/Bus2IP_RdCE Memory Mapped Register
// "1000" C_BASEADDR + 0x0
// "0100" C_BASEADDR + 0x4
// "0010" C_BASEADDR + 0x8
// "0001" C_BASEADDR + 0xC
//
// ------------------------------------------------------
assign
slv_reg_write_sel = Bus2IP_WrCE[1:0],
slv_reg_read_sel = Bus2IP_RdCE[1:0],
slv_write_ack = Bus2IP_WrCE[0] || Bus2IP_WrCE[1],
slv_read_ack = Bus2IP_RdCE[0] || Bus2IP_RdCE[1];
// implement slave model register(s)
always @( posedge Bus2IP_Clk )
begin
if ( Bus2IP_Resetn == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
end
else
begin
slv_reg0[7:0] <= data_recieved;
slv_reg0[8] <= rec_succ;
slv_reg0[9] <= uart_busy;
slv_reg[10] <= error;
case ( slv_reg_write_sel )
2'b10 :
for ( byte_index = 0; byte_index <= (C_SLV_DWIDTH/8)-1; byte_index = byte_index+1 )
if ( Bus2IP_BE[byte_index] == 1 )
slv_reg0[(byte_index*8) +: 8] <= Bus2IP_Data[(byte_index*8) +: 8];
2'b01 :
for ( byte_index = 0; byte_index <= (C_SLV_DWIDTH/8)-1; byte_index = byte_index+1 )
if ( Bus2IP_BE[byte_index] == 1 )
slv_reg1[(byte_index*8) +: 8] <= Bus2IP_Data[(byte_index*8) +: 8];
default : begin
slv_reg0 <= slv_reg0;
slv_reg1 <= slv_reg1;
end
endcase
end
end // SLAVE_REG_WRITE_PROC
// implement slave model register read mux
always @( slv_reg_read_sel or slv_reg0 or slv_reg1 )
begin
case ( slv_reg_read_sel )
2'b10 : slv_ip2bus_data <= slv_reg0;
2'b01 : slv_ip2bus_data <= slv_reg1;
default : slv_ip2bus_data <= 0;
endcase
end // SLAVE_REG_READ_PROC
// ------------------------------------------------------------
// Example code to drive IP to Bus signals
// ------------------------------------------------------------
assign IP2Bus_Data = (slv_read_ack == 1'b1) ? slv_ip2bus_data : 0 ;
assign IP2Bus_WrAck = slv_write_ack;
assign IP2Bus_RdAck = slv_read_ack;
assign IP2Bus_Error = 0;
endmodule
Приложение 1. Создание проекта в Xilinx Platform Studio