手搓8080接口,封装成AXI4‐ip,用zynq驱动(一) - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
手搓8080接口,封装成AXI4-ip,用zynq驱动(一)
1)说明
- st7796是sitronix公司经典的小尺寸lcd_driver驱动芯片
- 手头的这款屏尺寸是3.5寸,分辨率是320x480
- 这个模组的引脚定义如下。这次我们选择的是8080接口16bits模式,也就是im[2:0]配置成3'b010
2)fmc16b代码
代码的构思过程
-
1.1 序列机or状态机? 用序列机不太好,因此存在等待状态。等待状态是等待xfer_start以进行下一次读写。因此序列长度是不固定的。全局控制用状态机更好。序列机还是要的,用在局部,控制一次传输的节拍产生。一次传输节拍是确定的。
-
1.2 几个状态?IDLE,SEND_CMD, SEND_DATA, SEND_WAIT。注意这里存在一个等待状态。当传输一定长度数据时,必须要等待数据传完,才能拉高CS。整个传输期间CS为低。
-
1.3 几个控制信号? xfer_start由外部上位机给。xfer_end由内部产生,每次传输完成产生一个xfer_end通知上位机。xfer_start/xfer_end形成交互。
-
1.4 几个计数器?lsm_cnt用于一次传输的内部节拍产生,相当于是局部序列机。xfer_cnt是一个减计数器,用于记录传输次数。当xfer_cnt为0时表示传输完成。
-
1.5 状态机的跳转条件(xfer_start, xfer_end, xfer_cnt): IDLE状态,当收到xfer_start则进入SEND_CMD状态。 SEND_CMD状态,当收到xfer_end时,如果xfer_cnt=0则进入IDLE状态(xfer_cnt=0表示只发cmd),否则进入SEND_WAIT状态。 SEND_DATA状态,当收到xfer_end时,如果xfer_cnt=1则进入IDLE状态(表示最后一个数据已发完,为什么这里是=1与xfer_cnt的产生条件有关系),否则进入SEND_WAIT状态。 SEND_WAIT状态,当收到xfer_start时,进入SEND_DATA状态
-
1.6 xfer_end的产生条件: 与wr=1的时刻一致,需要根据序列机来产生。通过仿真看波形图来调整。 由于xfer_end的宽度是一个脉宽,因此不能与wrx/rdx/csx等信号放在一起产生,而是需要单独一个always产生。
-
1.7 输出csx/dcx/wrx/rdx/do等信号的产生 比较简单,根据时序图,用excel表列出每拍干什么就可以了。根据仿真微调一下。
-
1.7 xfer_cnt的计数条件 IDLE状态,且收到xfer_start时,将length赋给xfer_cnt作为初值。 SEND_DATA状态,每次收到xfer_end时,xfer_cnt=xfer_cnt-1
-
1.8 lsm_cnt的计数条件 lsm_cnt定位为局部计数器,因此位宽很小。 IDLE和SEND_WAIT状态清零,也就是不计数。 否则(SEND_CMD, SEND_DATA状态)计数。这个计数器让它自然计数,不需要设置清零,因为由于每次发送需要多少个cycle是确定的,lsm_cnt不会等到溢出就被清零了,因为跳转到SEND_WAIT状态或IDLE状态就会自动清零。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/02/23 16:19:06
// Design Name:
// Module Name: fmc
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module fmc(
input sys_clk,
input sys_rst_n,
input xfer_start,
output reg xfer_end,
input [15:0] data_in, //cmd or data
input [17:0] length,
output reg csx,
output reg dcx,
output reg wrx,
output reg rdx,
output reg [15:0] do
);
parameter LSM_MAX=4'd15;
reg [3:0] lsm_cnt;
parameter IDLE=2'b00;
parameter SEND_CMD=2'b01;
parameter SEND_DATA=2'b10;
parameter SEND_WAIT=2'b11;
reg [1:0] state;
reg [17:0] xfer_cnt;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
state<=IDLE;
else case(state)
IDLE: if(xfer_start)
state<=SEND_CMD;
SEND_CMD: if(xfer_end && xfer_cnt==0)
state<=IDLE;
else if(xfer_end)
state<=SEND_WAIT;
SEND_DATA: if(xfer_end && xfer_cnt==1)
state<=IDLE;
else if(xfer_end)
state<=SEND_WAIT;
SEND_WAIT: if(xfer_start)
state<=SEND_DATA;
default: state<=IDLE;
endcase
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
xfer_cnt<=18'd0;
else if(state==IDLE && xfer_start)
xfer_cnt<=length;
else if(state==SEND_DATA && xfer_end)
xfer_cnt<=xfer_cnt-1;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
lsm_cnt<=0;
else if(state==IDLE || state==SEND_WAIT)
lsm_cnt<=0;
else
begin
if(lsm_cnt==LSM_MAX-1)
lsm_cnt<=0;
else
lsm_cnt<=lsm_cnt+1;
end
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0) begin
csx<=1;
dcx<=1;
wrx<=1;
do<=16'h00;
end
else case(state)
IDLE: csx<=1; //begin and final state
SEND_CMD: begin
if(length==0)
case(lsm_cnt)
0: begin csx<=0; dcx<=0; wrx<=0; do<=data_in; end
1: begin wrx<=1;end
2: begin csx<=1;end
endcase
else
case(lsm_cnt)
0: begin csx<=0; dcx<=0; wrx<=0; do<=data_in; end
1: begin wrx<=1;end
endcase
end
SEND_DATA: begin
dcx<=1;
if(lsm_cnt==0)
begin wrx<=0; do<=data_in; end
else if(lsm_cnt==1)
wrx<=1;
end
SEND_WAIT: csx<=0;
endcase
//xfer_end要单独产生,因为它只占一个aclk脉宽,与其它信号不一样
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0) begin
xfer_end<=0;
end
else if(state==SEND_CMD && length==0 && lsm_cnt==2)
xfer_end<=1;
else if(state==SEND_CMD && length>0 && lsm_cnt==1)
xfer_end<=1;
else if(state==SEND_DATA && lsm_cnt==1)
xfer_end<=1;
else
xfer_end<=0;
endmodule
3)写testbench验证裸核读写(波形略)
`timescale 1ns / 1ps
module tb;
reg sys_clk, sys_rst_n;
initial sys_clk=0;
always #10 sys_clk=~sys_clk;
initial begin
sys_rst_n=0;
#1000;
sys_rst_n=1;
end
bit xfer_start;
bit [15:0] data_in;
bit [15:0] length;
initial begin
wait(sys_rst_n==1);
#100;
data_in=16'h2A;
length=0;
xfer_start=0; #20; xfer_start=1; #30; xfer_start=0;
#5000;
data_in=16'h36;
length=4;
xfer_start=0; #20; xfer_start=1; #30; xfer_start=0;
wait(fmc_inst.state==2'b11);
data_in=16'h23;
xfer_start=0; #20; xfer_start=1; #30; xfer_start=0;
wait(fmc_inst.state==2'b11);
data_in=16'h34;
xfer_start=0; #20; xfer_start=1; #30; xfer_start=0;
wait(fmc_inst.state==2'b11);
data_in=16'h45;
xfer_start=0; #20; xfer_start=1; #30; xfer_start=0;
wait(fmc_inst.state==2'b11);
data_in=16'h56;
xfer_start=0; #20; xfer_start=1; #30; xfer_start=0;
end
fmc fmc_inst(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.xfer_start(xfer_start),
.data_in(data_in), //cmd or data
.length(length)
);
endmodule
4)写testbench验证裸核发图,并上板验证(波形略)
- 综合生成bitstream后,可以产生固定的单色图。
`timescale 1ns / 1ps
module fmc_wrapper(
input sys_clk,
input sys_rst_n,
output csx,
output dcx,
output wrx,
output rdx,
output [15:0] do,
output [2:0] im,
output rst_o
);
assign im=3'b010;
assign rst_o=sys_rst_n;
parameter NDATA=320*480;
parameter XSTART=0, XEND=319;
parameter YSTART=0, YEND=479;
parameter RED = 16'hF800,
ORANGE = 16'hFC00,
YELLOW = 16'hFFE0,
GREEN = 16'h07E0,
CYAN = 16'h07FF,
BLUE = 16'h001F,
PURPPLE = 16'hF81F,
BLACK = 16'h0000,
WHITE = 16'hFFFF,
GRAY = 16'hD69A;
parameter DIV_MAX=5-1;
parameter LSM_MAX=NDATA+20-1;
reg xfer_start;
wire xfer_end;
reg [15:0] data_in;
reg [17:0] length;
reg [25:0] div_cnt;
reg [17:0] lsm_cnt;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
div_cnt<=0;
else if(div_cnt==DIV_MAX)
div_cnt<=0;
else
div_cnt<=div_cnt+1;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
lsm_cnt<=0;
else if(div_cnt==DIV_MAX && lsm_cnt==LSM_MAX)
lsm_cnt<=LSM_MAX;
else if(div_cnt==DIV_MAX)
lsm_cnt<=lsm_cnt+1;
reg [2:0] color_index;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
color_index<=0;
else if(div_cnt==DIV_MAX && lsm_cnt==LSM_MAX)
color_index<=color_index+1;
reg [15:0] color;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0)
color<=16'h0000;
else case(color_index)
0: color<= RED;
1: color<= GREEN;
2: color<= BLUE;
3: color<= YELLOW;
4: color<= PURPPLE;
default: color<= WHITE;
endcase
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==0) begin
xfer_start<=0;
data_in<=16'h00;
length<=16'd0;
end
else if(div_cnt==DIV_MAX) begin
case(lsm_cnt)
0: begin length<=0; data_in<=16'h11; xfer_start<=1; end
1: begin length<=0; data_in<=16'h29; xfer_start<=1; end
2: begin length<=1; data_in<=16'h36; xfer_start<=1; end
3: begin data_in<=16'h48; xfer_start<=1; end
4: begin length<=1; data_in<=16'h3a; xfer_start<=1; end
5: begin data_in<=16'h05; xfer_start<=1; end
6: begin length<=4; data_in<=16'h2a; xfer_start<=1; end
7: begin data_in<=XSTART/256; xfer_start<=1; end
8: begin data_in<=XSTART%256; xfer_start<=1; end
9: begin data_in<=XEND/256; xfer_start<=1; end
10: begin data_in<=XEND%256; xfer_start<=1; end
11: begin length<=4; data_in<=16'h2b; xfer_start<=1; end
12: begin data_in<=YSTART/256; xfer_start<=1; end
13: begin data_in<=YSTART%256; xfer_start<=1; end
14: begin data_in<=YEND/256; xfer_start<=1; end
15: begin data_in<=YEND%256; xfer_start<=1; end
16: begin length<=NDATA; data_in<=16'h2c; xfer_start<=1; end
default: begin data_in<=GREEN; xfer_start<=1; end
endcase
end
else
xfer_start<=0;
fmc fmc_inst (
// Input Ports - Single Bit
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.xfer_start (xfer_start),
// Input Ports - Busses
.data_in (data_in),
.length (length),
// Output Ports - Single Bit
.csx (csx),
.dcx (dcx),
.rdx (rdx),
.wrx (wrx),
.xfer_end (xfer_end),
// Output Ports - Busses
.do (do)
// InOut Ports - Single Bit
// InOut Ports - Busses
);
endmodule
`timescale 1ns / 1ps
module fmc_wrapper_test;
reg sys_clk, sys_rst_n;
initial sys_clk=0;
always #10 sys_clk=~sys_clk;
initial begin
sys_rst_n=0;
#1000;
sys_rst_n=1;
end
fmc_wrapper fmc_wrapper_inst(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n)
);
defparam fmc_wrapper_inst.NDATA=10;
endmodule
5)封装成AXI4-ip
关键代码:接口,寄存器读写,例化,如何产生xfer_start和使用xfer_end
// Users to add ports here
output csx,
output dcx,
output wrx,
output rdx,
output [15:0] do,
output xfer_start,
output xfer_end,
// User ports ends
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
slv_reg0 <= 0;
slv_reg1 <= 0;
slv_reg2 <= 0;
slv_reg3 <= 0;
slv_reg4 <= 0;
slv_reg5 <= 0;
slv_reg6 <= 0;
slv_reg7 <= 0;
end
if(xfer_end) begin
slv_reg3<=32'h1;
end
// Add user logic here
/*
slv_reg0 => xfer_start
slv_reg1 => data_in
slv_reg2 => length
slv_reg3[0] => xfer_end
*/
reg xfer_start;
always @( posedge S_AXI_ACLK )
if ( S_AXI_ARESETN == 1'b0 )
xfer_start<= 0;
else
xfer_start <= (slv_reg_wren && axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]==2'h0)? 1:0;
fmc fmc_inst (
// Input Ports - Single Bit
.sys_clk (S_AXI_ACLK),
.sys_rst_n (S_AXI_ARESETN),
.xfer_start (xfer_start),
// Input Ports - Busses
.data_in (slv_reg1[15:0]),
.length (slv_reg2[17:0]),
// Output Ports - Single Bit
.csx (csx),
.dcx (dcx),
.rdx (rdx),
.wrx (wrx),
.xfer_end (xfer_end),
// Output Ports - Busses
.do (do)
// InOut Ports - Single Bit
// InOut Ports - Busses
);
// User logic ends
6)用VIP验证
- 代码的特别之处:我们使用了task,以简化重复的操作。
`timescale 1ns / 1ps
//此处import报错为vivado bug,不影响仿真
//Import two required packages: axi_vip_pkg and <component_name>_pkg.
//how to get component_name? use tcl command: get_ips *vip*
import axi_vip_pkg::*;
import system_axi_vip_0_0_pkg::*;
module my_sim_top;
bit sys_clk;
bit sys_rst_n;
bit csx,dcx,rdx,wrx;
bit xfer_start,xfer_end;
bit [15:0] dout;
system_wrapper system_wrapper_i
(.csx(csx),
.dcx(dcx),
.dout(dout),
.rdx(rdx),
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.wrx(wrx),
.xfer_end(xfer_end),
.xfer_start(xfer_start));
//AXI4-Lite signals
xil_axi_resp_t resp;
bit[31:0] addr, data, base_addr = 32'h44A00000, switch_state;
parameter NDATA=320*480;
parameter XSTART=0, XEND=319;
parameter YSTART=0, YEND=479;
parameter RED = 16'hF800,
ORANGE = 16'hFC00,
YELLOW = 16'hFFE0,
GREEN = 16'h07E0,
CYAN = 16'h07FF,
BLUE = 16'h001F,
PURPPLE = 16'hF81F,
BLACK = 16'h0000,
WHITE = 16'hFFFF,
GRAY = 16'hD69A;
//创建时钟和复位信号
// Generate the clock : 50 MHz
initial sys_clk=0;
always #10ns sys_clk = ~sys_clk;
initial begin
//Assert the reset
sys_rst_n = 0;
#340ns
// Release the reset
sys_rst_n = 1;
end
//创建axi_vip的agent
//<component_name>_mst_t
system_axi_vip_0_0_mst_t master_agent;
initial begin
// Step 4 - Create a new agent
master_agent = new("master vip agent",system_wrapper_i.system_i.axi_vip_0.inst.IF);
// Step 5 - Start the agent
master_agent.start_master();
//Wait for the reset to be released
wait (sys_rst_n == 1'b1);
#1us
set_length(32'h00);
send_data(32'h11);
send_data(32'h29);
#1us
set_length(32'h01);
send_data(32'h36);
send_data(32'h48);
#1us
set_length(32'h01);
send_data(32'h3a);
send_data(32'h05);
#1us
set_length(32'h04);
send_data(32'h2a);
send_data(XSTART/256);
send_data(XSTART%256);
send_data(XEND/256);
send_data(XEND%256);
#1us
set_length(32'h04);
send_data(32'h2b);
send_data(YSTART/256);
send_data(YSTART%256);
send_data(YEND/256);
send_data(YEND%256);
#1us
set_length(NDATA+4);
send_data(32'h2c);
for(int i=0; i<NDATA; i++) begin
send_data(RED);
end
end
task set_length(input logic [31:0] data);
// set length
addr = 32'h08;
master_agent.AXI4LITE_WRITE_BURST(base_addr + addr, 0, data, resp);
endtask
task send_data(input logic [31:0] data);
// set cmd
addr = 32'h04;
master_agent.AXI4LITE_WRITE_BURST(base_addr + addr, 0, data, resp);
// xfer_start
addr = 32'h00;
data = 32'h01;
master_agent.AXI4LITE_WRITE_BURST(base_addr + addr, 0, data, resp);
// wait xfer_end
wait(xfer_end == 1);
endtask
// initial begin
// $monitor("Signal tx changed to %b at time %t", tx, $time);
// end
endmodule
我们看到,如下0x2a指令的执行,实际需要85个cycles。
set_length(32'h04);
send_data(32'h2a);
send_data(XSTART/256);
send_data(XSTART%256);
send_data(XEND/256);
send_data(XEND%256);
7)底层波形
fmc16底层波形,发一条纯指令0x11的执行过程
fmc16底层波形,发一条配寄存器{0x36, 0x48}的执行过程
8)状态转移图
+----------+ +-------------+
| | | |
| IDLE | -- xfer_start --> | SEND_CMD |
| | | |
+----------+ +-------------+
|
xfer_end=1
v
+-----------+
| |
| SEND_WAIT |
| |
+-----------+
|
xfer_start
v
+------------+
| |
| SEND_DATA |
| |
+------------+
|
xfer_end=1
v
+-----------+
| |
| SEND_WAIT |
| |
+-----------+