简易APB4 slave实践

   一个简易的(不完整的)APB4 slave的可以没有PREADY和PSLVERR,这两个信号都被赋予常数,以及没有PPROT。

   两种不同类型的寄存器:

       图: 普通寄存器电路图

           图: 带读写控制寄存器电路图

                        图:带读写控制寄存器时序图

 

 一般来讲,一个模块的interface到内部reg之间,需要的信号为地址信号addr,读写使能信号(分开),byte_strobe字节选通信号,读写数据信号(分开)。

 interface spec:

寄存器表: 

 

 部分代码:

 1 module apb4_slave #(
 2  parameter ADDRWIDTH = 12)
 3 (
 4     input     wire               PCLK,
 5     input     wire               PRESETn,
 6 
 7     input    wire                    PSEL,
 8     input    wire[ADDRWIDTH -1:0]    PADDR,
 9     input    wire                    PENABLE,
10     input    wire                    PWRITE,
11     input    wire[31:0]              PWDATA,
12     input    wire[3:0]               PSTRB,
13 
14     input    wire[3:0]                ECOREVNUM,
15     
16     output    wire[31:0]              PRDATA,
17     output    wire                    PREADY,
18     output    wire                    PSLVERR
19 );
20 21   wire  [ADDRWIDTH-1:0]  reg_addr;
22   wire             reg_read_en;
23   wire             reg_write_en;
24   wire  [3:0]          reg_byte_strobe;
25   wire  [31:0]        reg_wdata;
26   wire   [31:0]        reg_rdata;
27 28 apb4_slave #(.ADDRWIDTH  (ADDRWIDTH))
29   u_apb_slave_interface(
30 .pclk        (PCLK),
31 .presetn      (PRESETn),
32 
33 .psel        (PSEL),
34 .paddr        (PADDR),
35 .penable      (PENABLE),
36 .pwrite       (PWRITE),
37 .pwdata       (PWDATA),
38 .pstrb       (PSTRB),
39 
40 .prdata       (PRDATA),
41 .pready       (PREADY),
42 .pslverr      (PSLVERR),
43 
44 .addr        (reg_addr),
45 .read_en       (reg_read_en),
46 .write_en      (reg_write_en),
47 .byte_strobe    (reg_byte_strobe),
48 .wdata        (reg_wdata),
49 .tdata        (reg_rdata)
50 
51 );

 

 

u_apb_slave_interfacef #(
    parameter    ADDRWIDTH = 12)
(
    // IO declaration
    input    wire                pclk,
    input    wire                presetn,
    // apb interface input
    input    wire                psel,
    input    wire[ADDRWIDTH-1:0]    paddr,
    input    wire                penable,
    input    wire                pwite,
    input    wire[31:0]            pwdata,
    input    wire[3:0]            pstrb,
    // apb interface output
    output     wire[31:0]            prdata,
    output     wire                pready,
    // Register interface
    output    wire[ADDRWIDTH-1:0]    addr,
    output    wire                read_en,
    output    wire                write_en,
    output    wire[3:0]            byte_pstrb,
    output    wire[31:0]            wdata,
    output    wire[31:0]            rdata
);

assign     pready = 1'b1;
assign     pslverr = 1'b0;

assign    addr = paddr;
assign    read_en = psel & (~pwrite);                // 当pwrite为0,psel为1的时候,才读
assign    write_en = psel & (~penable) & pwrite;     // 在PCLK中,第一拍为psel有效,penable为低,第二拍才是psel和penable同时有效;

assign    byte_pstrb = pstrb;
assign    wdata = pwdata;
assign    prdata = rdata;

 

 

module apb4_slave_reg #(
    parameter    ADDRWIDTH = 12)
(
    input    wire                pclk,
    input    wire                presetn,

    input    wire[ADDRWIDTH-1:0]    addr,
    input    wire                read_en,
    input    wire                write_en,
    input    wire[3:0]            byte_pstrb,
    input    wire[31:0]            wdata,
    input    wire                ecorevnum,
    output    wire[31:0]            rdata

);

localparam ARM_CMSDK_APB4_EG_SLAVE_PID4    = 32'h00000004; //    12'hFD0;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID5    = 32'h00000000; //    12'hFD4;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID6    = 32'h00000000; //    12'hFD8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID7    = 32'h00000000; //    12'hFDC;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID0    = 32'h00000019; //    12'hFE0;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID1    = 32'h000000B8; //    12'hFE4;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID2    = 32'h0000001B; //    12'hFE8;
localparam ARM_CMSDK_APB4_EG_SLAVE_PID3    = 32'h00000000; //    12'hFEC;

wire        [3:0]        wr_sel;

// 取地址的高10位出来,why?
// 地址是32为对其的,末尾都是0(0000)、4(0100)、8(1000)、C(1100)循环的,低两位都是一样的,只有高10位不一样
assign     wr_sel[0] = ((addr[(ADDRWIDTH-1):2]==10'b0000000000)&(write_en)) ? 1'b1: 1'b0;
assign     wr_sel[1] = ((addr[(ADDRWIDTH-1):2]==10'b0000000001)&(write_en)) ? 1'b1: 1'b0;
assign     wr_sel[2] = ((addr[(ADDRWIDTH-1):2]==10'b0000000010)&(write_en)) ? 1'b1: 1'b0;
assign     wr_sel[3] = ((addr[(ADDRWIDTH-1):2]==10'b0000000011)&(write_en)) ? 1'b1: 1'b0;
 
// write_en = psel & (~penable) & pwrite; 时序要求在penable为高这一拍把数据写下去,所以要在其前一拍判断是否要写。

// 寄存器的写操作
// Data register: data0
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data0 <= {32{1'b0}};
    end 
    else if (wr_sel[0]) begin
        if (byte_strobe[0])
            data0[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data0[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data0[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data0[31:24] <= wdata[31:24];
    end
end
// Data register: data1
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data1 <= {32{1'b0}};
    end 
    else if (wr_sel[1]) begin
        if (byte_strobe[0])
            data1[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data1[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data1[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data1[31:24] <= wdata[31:24];
    end
end
// Data register: data2
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data2 <= {32{1'b0}};
    end 
    else if (wr_sel[2]) begin
        if (byte_strobe[0])
            data2[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data2[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data2[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data2[31:24] <= wdata[31:24];
    end
end
// Data register: data3
always @(posedge pclk or negedge presetn) begin
    if (~presetn) begin
        data3 <= {32{1'b0}};
    end 
    else if (wr_sel[1]) begin
        if (byte_strobe[0])
            data3[ 7:0] <= wdata[7:0];
        if (byte_strobe[1])
            data3[15:8] <= wdata[15:8];
        if (byte_strobe[2])
            data3[23:16] <= wdata[23:16];
        if (byte_strobe[3])
            data3[31:24] <= wdata[31:24];
    end
end

// 寄存器的读操作
always @(read_en or    addr or    data0 or data1 or data2 or data3 or ecorevnum) begin
    case (read_en)
    1'b1: begin 
        if (addr[11:4] == 8'h00) begin        // 判断为RW类型的寄存器
            case (addr[3:2])
                2'b00: rdata = data0;
                2'b01: rdata = data1;
                2'b10: rdata = data2;
                2'b11: rdata = data3;
                default: rdata = {32{1'bx}};
            endcase
        end
        else if (addr[11:6] == 6'h3F) begin     // 判断为RO类型的寄存器
            case(addr[5:2])
            4'b0100:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID4;
            4'b0101:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID5;
            4'b0110:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID6;
            4'b0111:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID7;
            4'b1000:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID0;
            4'b1001:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID1;
            4'b1010:rdata = ARM_CMSDK_APB4_EG_SLAVE_PID2;
            4'b1011:rdata = {ARM_CMSDK_APB4_EG_SLAVE_PID3[31:0],ecorevnum[3:0],4'h0};
            default: rdata = {32{1'bx}};
            endcase
        end 
        else begin
            rdata = {32{1'b0}};
        end 
    end
    1'b0: begin
        rdata = {32{1'b0}};
    end 
    default: rdata = {32{1'bx}};
    endcase
end 

endmodule

 

转载于:https://www.cnblogs.com/yiwenbo/p/10047063.html

APB (Advanced Peripheral Bus)是ARM架构中使用的一种外设总线。APB在系统-on-chip (SOC)设计中具有重要的作用,通常连接多个外设到总线上。APB 3 Slave是指按照APB3规范设计的外设,下面是一个APB 3 Slave Verilog代码示例。 module apb_slave ( input wire PCLK, //APB总线时钟信号 input wire PRESETn, //异步复位信号 input wire PSELn, //片选信号 input wire PADDR, //地址信号 input wire PWRITE, //写数据使能 input wire [31:0] PWDATA, //写入数据 output wire [31:0] PRDATA, //读取数据 output wire PREADY, //读写完成信号 output wire PSLVERROR //错误信号 ); reg [31:0] my_register; //用于存储外设的寄存器 always @(posedge PCLK or negedge PRESETn) begin if (!PRESETn) begin //复位信号触发 my_register <= 32'h0; end else if (PSELn && !PWRITE) begin //读取操作 PRDATA <= my_register; end else if (PSELn && PWRITE) begin //写入操作 my_register <= PWDATA; end end assign PREADY = (PSELn && (PADDR == 32'h0)) ? 1'b1 : 1'b0; //读写完成信号 assign PSLVERROR = 1'b0; //错误信号始终为0 endmodule 以上代码所示为一个APB 3 Slave外设模块,其中包括一个用于存储外设寄存器的32位寄存器"my_register",根据输入的APB总线信号和地址信号,进行读取或写入操作,并将结果存储在相应的寄存器中。 当复位信号PRESETn为低电平时,所有寄存器被清零。当外设被选中PSELn为高电平且是读取操作时,将my_register的值输出到PRDATA中;当外设被选中且是写入操作时,将PWDATA写入my_register。 PREADY用于表示读写操作的完成状态,当外设被选中且地址为0时,PREADY为高电平,表示读写操作完成;否则为低电平。PSLVERROR始终为低电平,表示没有错误发生。 这是一个简化的示例,实际的APB 3 Slave外设可能包含更多的寄存器和逻辑。以上代码提供了一个基本的结构,可以根据实际需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值