LPC总线设计及其仿真验证
简述
因为项目只用到了IO cycle,故只对IO Write/Read进行设计
时序
Typical Timing for LFRAME

Extended Timing for LFRAME

Abort Mechanism

正常情况下,START只存在1个clock,EXTEND情况是2个clock(代码里已考虑到)。
按照LDC规范的表述,Abort是在Sync状态发生的,也就是在Data状态之前,此次传输无效,如果发生在Data则按正常传输进行
peripheral代码
点击查看代码
`resetall
`timescale 1ns / 1ns
`default_nettype none
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/07/21 08:56:23
// Design Name:
// Module Name: lpc
// Project Name:
// Target Devices:
// Tool Versions:
// Description: limited for I/O read/write
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
`include "apb_lpc_defines.vh"
module lpc_peripheral(
// LPC
input wire lclk ,
input wire lreset_n ,
input wire lframe ,
output reg lad_oe ,
input wire [3:0] lad_in ,
output reg [3:0] lad_out ,
// User interface
output reg m_apb_lpc_penable ,
output reg m_apb_lpc_psel ,
output reg m_apb_lpc_write ,
output reg [15:0] m_apb_lpc_paddr ,
output wire [ 7:0] m_apb_lpc_wdata ,
input wire [ 7:0] m_apb_lpc_rdata ,
input wire m_apb_lpc_pready
);
/*-------------------------------------------------------------------------------
-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------
-------------------------------------------------------------------------------*/
reg [13:0] state_reg = `LPC_ST_START, state_next;
reg [ 1:0] count_reg = 2'b0, count_next;
reg [15:0] lpc_addr = 16'h0, lpc_addr_next;
reg [ 7:0] lpc_wdata = 8'h0, lpc_wdata_next;
reg [ 7:0] lpc_rdata;
reg rw_indicator,rw_indicator_next; // 1 for write, 0 for read
/*-------------------------------------------------------------------------------
-------------------------------------------------------------------------------*/
always @(*) begin
state_next = state_reg;
lpc_addr_next = lpc_addr;
lpc_wdata_next = lpc_wdata;
rw_indicator_next = rw_indicator;
count_next = 2'b0;
lad_oe = 4'b0;
lad_out = `LPC_STOP;
case (state_reg)
`LPC_ST_START : begin
rw_indicator_next = 1'b0;
lpc_wdata_next = 8'h0;
lpc_addr_next = 16'h0;
if (~lframe) begin
state_next = (lad_in == `LPC_START)? `LPC_ST_CYCTYP:`LPC_ST_START;
end else begin
state_next = `LPC_ST_START;
end
end
`LPC_ST_CYCTYP : begin
if (~lframe) begin
state_next = `LPC_ST_CYCTYP;
end else begin
state_next = `LPC_ST_ADDR;
if (lad_in[1]) begin
rw_indicator_next = 1'b1;
end else begin
rw_indicator_next = 1'b0;
end
end
end
`LPC_ST_ADDR : begin
count_next = count_reg + 1;
case (count_reg)
0: lpc_addr_next[15:12] = lad_in;
1: lpc_addr_next[11: 8] = lad_in;
2: lpc_addr_next[ 7: 4] = lad_in;
3: lpc_addr_next[ 3: 0] = lad_in;
endcase
if (count_reg == 2'h3)
state_next = `LPC_ST_H_TAR1;
else begin
state_next = `LPC_ST_ADDR;
end
end
`LPC_ST_H_TAR1 : begin
state_next = `LPC_ST_H_TAR2;
end
`LPC_ST_H_TAR2 : begin // host float LAD
state_next = `LPC_ST_SYNC1;
end
`LPC_ST_SYNC1 : begin
lad_oe = 1'b1;
lad_out = `LPC_SYNC_SWAIT;
if (~lframe)
state_next = `LPC_ST_START;
else
state_next = `LPC_ST_SYNC2;
end
`LPC_ST_SYNC2 : begin
if (~lframe) begin
state_next = `LPC_ST_START;
end else begin
lad_oe = 1'b1;
lad_out = `LPC_SYNC_READY;
if (rw_indicator)
state_next = `LPC_ST_H_DATA;
else
state_next = `LPC_ST_P_DATA;
end
end
`LPC_ST_H_DATA : begin
count_next = count_reg + 1;
if (count_reg == 0) begin
lpc_wdata_next[3:0] = lad_in;
end else begin
lpc_wdata_next[7:4] = lad_in;
state_next = `LPC_ST_P_TAR1;
end
end
`LPC_ST_P_DATA : begin
count_next = count_reg + 1;
lad_oe = 1'b1;
if (count_reg == 0) begin
lad_out = lpc_rdata[3:0];
end else begin
lad_out = lpc_rdata[7:4];
state_next = `LPC_ST_P_TAR1;
end
end
`LPC_ST_P_TAR1 : begin
lad_oe = 1'b1;
lad_out = `LPC_STOP;
state_next = `LPC_ST_P_TAR2;
end
`LPC_ST_P_TAR2 : begin
lad_oe = 1'b0; // peripherals float LAD
state_next = `LPC_ST_START;
end
default: state_next = `LPC_ST_START;
endcase
end
always @(posedge lclk or negedge lreset_n) begin
if (~lreset_n) begin
state_reg <= `LPC_ST_START;
count_reg <= 2'h0;
lpc_addr <= 16'h0;
rw_indicator <= 1'b0;
lpc_wdata <= 8'h0;
end else begin
state_reg <= state_next;
count_reg <= count_next;
lpc_addr <= lpc_addr_next;
lpc_wdata <= lpc_wdata_next;
rw_indicator <= rw_indicator_next;
end
end
wire host_write = m_apb_lpc_penable & m_apb_lpc_psel & m_apb_lpc_write & m_apb_lpc_pready;
wire host_read = m_apb_lpc_penable & m_apb_lpc_psel & (~m_apb_lpc_write) & m_apb_lpc_pready;
assign m_apb_lpc_wdata = lpc_wdata;
always @(posedge lclk) begin
if (~lreset_n) begin
m_apb_lpc_penable <= 1'b0;
m_apb_lpc_psel <= 1'b0;
m_apb_lpc_paddr <= 16'h0;
m_apb_lpc_write <= 1'b0;
lpc_rdata <= 8'h0;
end else begin
if (rw_indicator) begin
if (state_reg == `LPC_ST_H_DATA) begin
m_apb_lpc_psel <= 1'b1;
m_apb_lpc_penable <= 1'b0;
m_apb_lpc_write <= 1'b1;
end else if (state_reg == `LPC_ST_P_TAR1) begin
m_apb_lpc_psel <= 1'b1;
m_apb_lpc_penable <= 1'b1;
m_apb_lpc_write <= 1'b1;
end else begin
m_apb_lpc_penable <= 1'b0;
m_apb_lpc_psel <= 1'b0;
m_apb_lpc_write <= 1'b0;
end
end else begin
if (state_reg == `LPC_ST_H_TAR2) begin
m_apb_lpc_psel <= 1'b1;
m_apb_lpc_penable <= 1'b0;
m_apb_lpc_write <= 1'b0;
end else if (state_reg == `LPC_ST_SYNC1) begin
m_apb_lpc_psel <= 1'b1;
m_apb_lpc_penable <= 1'b1;
m_apb_lpc_write <= 1'b0;
end else begin
m_apb_lpc_penable <= 1'b0;
m_apb_lpc_psel <= 1'b0;
m_apb_lpc_write <= 1'b0;
end
end
m_apb_lpc_paddr <= lpc_addr;
if (host_read) begin
lpc_rdata <= m_apb_lpc_rdata;
end
end
end
endmodule
`resetall
搭建Host进行仿真
host task代码搭建
点击查看代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/07/24 16:16:14
// Design Name:
// Module Name: lpc_host
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module lpc_host(
input wire lclk ,
output reg lframe ,
output reg lad_oe ,
output reg [3:0] lad_o ,
input wire [3:0] lad_i
);
/*--------------------------------------------------------------------------------
--------------------------------------------------------------------------------*/
`define LPC_START 4'h0
`define LPC_STOP 4'hf
`define LPC_SWAIT 4'h5
`define LPC_READY 4'h0
`define IO_WRITE 4'h2
`define IO_READ 4'h0
integer index = 0;
/*--------------------------------------------------------------------------------
--------------------------------------------------------------------------------*/
task Start;
begin
@(posedge lclk);
lframe = 1'b1;
lad_oe = 1'b1;
lad_o = `LPC_STOP;
@(posedge lclk);
lframe = 1'b0;
lad_oe = 1'b1;
lad_o = `LPC_START;
end
endtask
task Start_extend;
begin
@(posedge lclk);
lframe = 1'b1;
lad_oe = 1'b1;
lad_o = `LPC_STOP;
@(posedge lclk);
lframe = 1'b0;
lad_oe = 1'b1;
lad_o = `LPC_START;
@(posedge lclk);
end
endtask
task Cyctype;
input [3:0] dir_type;
begin
@(posedge lclk);
lframe = 1'b1;
lad_oe = 1'b1;
lad_o = dir_type;
end
endtask
task Addr;
input [15:0] host_addr;
begin
while(index < 4) begin
@(posedge lclk);
lad_oe = 1'b1;
lad_o = host_addr[(15-index*4)-:4];
index = index + 1;
end
end
endtask
task Tar_h;
begin
@(posedge lclk);
lad_oe = 1'b1;
lad_o = 4'b1111;
// turn-around
@(posedge lclk);
lad_oe = 1'b0;
lad_o = 4'b1111;
end
endtask
task Sync;
begin
repeat(2) begin
@(posedge lclk);
end
end
endtask
task Sync_abort;
begin
repeat(2) begin
@(posedge lclk);
end
lframe = 1'b0;
end
endtask
task Host_write;
input [7:0] host_dout;
begin
@(posedge lclk);
lad_oe = 1'b1;
lad_o = host_dout[3:0];
@(posedge lclk);
lad_oe = 1'b1;
lad_o = host_dout[7:4];
end
endtask
task Host_read;
output [7:0] host_din;
begin
@(posedge lclk);
lad_oe = 1'b0;
host_din[3:0] = lad_i;
@(posedge lclk);
host_din[7:4] = lad_i;
end
endtask
task Tar_peripheral;
begin
@(posedge lclk);
lad_oe = 1'b1;
lad_o = `LPC_STOP;
@(posedge lclk);
lad_oe = 1'b0;
lad_o = `LPC_STOP;
index = 0;
end
endtask
/*--------------------------------------------------------------------------------
--------------------------------------------------------------------------------*/
task lpc_host_write(
input [15:0] host_addr ,
input [ 7:0] host_wdata
);
begin
Start();
Cyctype(`IO_WRITE);
Addr(host_addr);
Tar_h();
Sync();
Host_write(host_wdata);
Tar_peripheral();
end
endtask
task lpc_host_read(
input [15:0] host_addr ,
output [ 7:0] host_rdata
);
begin
Start();
Cyctype(`IO_READ);
Addr(host_addr);
Tar_h();
Sync();
Host_read(host_rdata);
Tar_peripheral();
end
endtask
task host_ex_write(
input [15:0] host_addr ,
input [ 7:0] host_wdata
);
begin
Start_extend();
Cyctype(`IO_WRITE);
Addr(host_addr);
Tar_h();
Sync();
Host_write(host_wdata);
Tar_peripheral();
end
endtask
task host_ex_read(
input [15:0] host_addr ,
output [ 7:0] host_rdata
);
begin
Start_extend();
Cyctype(`IO_READ);
Addr(host_addr);
Tar_h();
Sync();
Host_read(host_rdata);
Tar_peripheral();
end
endtask
task host_write_abort(
input [15:0] host_addr ,
input [ 7:0] host_wdata
);
begin
Start();
Cyctype(`IO_WRITE);
Addr(host_addr);
Tar_h();
Sync_abort();
end
endtask
task host_read_abort(
input [15:0] host_addr ,
output [ 7:0] host_rdata
);
begin
Start();
Cyctype(`IO_READ);
Addr(host_addr);
Tar_h();
Sync_abort();
end
endtask
initial begin
lframe = 1'b1;
lad_oe = 1'b1;
lad_o = 4'hf;
end
endmodule
仿真顶层
点击查看代码
`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2025/07/24 08:31:32
// Design Name:
// Module Name: tb_lpc
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module tb_lpc();
reg LCLK,LRESET_N;
wire LFRAME;
wire [3:0] LAD;
wire lad_oe;
wire [3:0] lad_o,lad_i;
reg serirq_oe,serirq_o;
wire serirq_i;
wire [31:0] IRQ = 32'h1234;
reg [ 7:0] host_di ;
always #15 LCLK = ~LCLK;
initial begin
LCLK = 1'b0;
LRESET_N = 1'b0;
host_di = 8'h0;
#200
LRESET_N = 1'b1;
#200
host.lpc_host_write(16'h0,8'h0);
host.lpc_host_write(16'h1,8'h1);
host.lpc_host_write(16'h2,8'h2);
host.lpc_host_write(16'h3,8'h3);
host.lpc_host_write(16'h4,8'h4);
host.lpc_host_write(16'h5,8'h5);
host.lpc_host_write(16'h6,8'h6);
host.lpc_host_write(16'h7,8'h7);
#200
host.host_ex_read(16'h0,host_di);
host.host_ex_read(16'h1,host_di);
host.host_ex_read(16'h2,host_di);
host.host_ex_read(16'h3,host_di);
host.host_ex_read(16'h4,host_di);
host.host_ex_read(16'h5,host_di);
host.host_ex_read(16'h6,host_di);
host.host_ex_read(16'h7,host_di);
#200
host.host_ex_write(16'h0,8'h7);
host.host_ex_write(16'h1,8'h6);
host.host_ex_write(16'h2,8'h5);
host.host_ex_write(16'h3,8'h4);
host.host_ex_write(16'h4,8'h3);
host.host_ex_write(16'h5,8'h2);
host.host_ex_write(16'h6,8'h1);
host.host_ex_write(16'h7,8'h0);
#200
host.lpc_host_read(16'h0,host_di);
host.lpc_host_read(16'h1,host_di);
host.lpc_host_read(16'h2,host_di);
host.lpc_host_read(16'h3,host_di);
host.lpc_host_read(16'h4,host_di);
host.lpc_host_read(16'h5,host_di);
host.lpc_host_read(16'h6,host_di);
host.lpc_host_read(16'h7,host_di);
#200
host.host_write_abort(16'h3,8'h3);
#200
host.host_read_abort(16'h4,host_di);
#2000
$stop;
end
/*---------------------------------------------------------------------
---------------------------------------------------------------------*/
genvar i;
generate
for (i = 0; i < 4; i=i+1) begin
pullup(LAD[i]);
end
endgenerate
pullup(SERIRQ);
assign lad_i = LAD;
assign LAD = (lad_oe)? lad_o : 4'bzzzz;
super_io_top super_io(
.LCLK (LCLK ),
.LRESET_N (LRESET_N),
.LFRAME (LFRAME ),
.LAD (LAD ),
.SERIRQ (SERIRQ ),
.IRQ (IRQ )
);
lpc_host host(
.lclk (LCLK ),
.lframe (LFRAME ),
.lad_oe (lad_oe ),
.lad_o (lad_o ),
.lad_i (lad_i )
);
endmodule
仿真结果
IO write部分波形

IO read部分波形

不知道为啥,读回来的结果高低位是反的,但代码没看出毛病

浙公网安备 33010602011771号