LPC总线设计及其仿真验证

简述

因为项目只用到了IO cycle,故只对IO Write/Read进行设计

时序

Typical Timing for LFRAME

image

Extended Timing for LFRAME

image

Abort Mechanism

image

正常情况下,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部分波形

image

IO read部分波形

image

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

posted @ 2025-07-24 17:55  AlwaysComb  阅读(31)  评论(0)    收藏  举报