1、AHB从机
AHB从机应答来自系统主主机发起的传输。从机使用从译码器输出的 HSELx 信号来决定它什么时候作应答。其它传输需要的信号,如地址与控制信息由主机产生。
下图为一个AHB从机的接口框图:
下面是一个AHB接口的SRAM控制器的程序:
//------------------------------------------------------------------------------
// File : ahb_sram.v
// Editor : VSCode, Tab Size(4)
// Description : Top Module of AHB SRAM Controller.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
module ahb_sram #(
parameter SYNC_RESET = 1,
parameter AHB_DWIDTH = 32,
parameter AHB_AWIDTH = 32,
parameter SIZE_IN_BYTES = 2048,
parameter ADD_WIDTH = $clog2(SIZE_IN_BYTES)
)
(
//----------------------------------
// IO Declarations
//----------------------------------
// Inputs
input HCLK,
input HRESETN,
input HSEL,
input HREADYIN,
input [1:0] HTRANS,
input [2:0] HBURST,
input [2:0] HSIZE,
input [AHB_DWIDTH-1:0] HWDATA,
input [AHB_AWIDTH-1:0] HADDR,
input HWRITE,
// Outputs
output [AHB_DWIDTH-1:0] HRDATA,
output [1:0] HRESP,
output HREADYOUT
);
//----------------------------------
// Variable Declarations
//----------------------------------
wire [ADD_WIDTH-1:0] HADDR_cal;
wire [2:0] ahbsram_size;
wire [ADD_WIDTH-1:0] ahbsram_addr;
wire [31:0] ahbsram_wdata;
wire ahbsram_write;
wire [31:0] sramahb_rdata;
wire sramahb_ack;
//----------------------------------
// Start of Main Code
//----------------------------------
assign HADDR_cal = HADDR[ADD_WIDTH-1:0];
// Instantiations
ahb_sram_if #(
.AHB_DWIDTH (AHB_DWIDTH),
.AHB_AWIDTH (AHB_AWIDTH),
.ADD_WIDTH (ADD_WIDTH),
.SYNC_RESET (SYNC_RESET)
)
u_ahb_sram_if (
.HCLK (HCLK),
.HRESETN (HRESETN),
.HSEL (HSEL),
.HTRANS (HTRANS),
.HBURST (HBURST),
.HWRITE (HWRITE),
.HWDATA (HWDATA),
.HSIZE (HSIZE),
.HADDR (HADDR_cal),
.HREADYIN (HREADYIN),
// From SRAM Control signals
.sramahb_ack (sramahb_ack),
.sramahb_rdata (sramahb_rdata),
// Outputs
.HREADYOUT (HREADYOUT),
.HRESP (HRESP),
// To SRAM Control signals
.ahbsram_req (ahbsram_req),
.ahbsram_write (ahbsram_write),
.ahbsram_wdata (ahbsram_wdata),
.ahbsram_size (ahbsram_size),
.ahbsram_addr (ahbsram_addr),
.HRDATA (HRDATA)
);
sram_ctrl_if #(
.ADD_WIDTH (ADD_WIDTH),
.SYNC_RESET (SYNC_RESET)
)
u_sram_ctrl_if (
.HCLK (HCLK),
.HRESETN (HRESETN),
// From AHB Interface signals
.ahbsram_req (ahbsram_req),
.ahbsram_write (ahbsram_write),
.ahbsram_wdata (ahbsram_wdata),
.ahbsram_size (ahbsram_size),
.ahbsram_addr (ahbsram_addr),
// Outputs
// To AHB Interface signals
.sramahb_ack (sramahb_ack),
.sramahb_rdata (sramahb_rdata)
);
endmodule
//------------------------------------------------------------------------------
// File : ahb_sram_if.v
// Editor : VSCode, Tab Size(4)
// Description :
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
module ahb_sram_if #(
parameter AHB_DWIDTH = 32,
parameter AHB_AWIDTH = 32,
parameter ADD_WIDTH = 11,
parameter SYNC_RESET = 0
)
(
//----------------------------------
// IO Declarations
//----------------------------------
// Inputs
input HCLK,
input HRESETN,
input HSEL,
input HREADYIN,
input [1:0] HTRANS,
input [2:0] HBURST,
input [2:0] HSIZE,
input [ADD_WIDTH-1:0] HADDR,
input [AHB_DWIDTH-1:0] HWDATA,
input HWRITE,
input sramahb_ack,
input [AHB_DWIDTH-1:0] sramahb_rdata,
// Outputs
output HREADYOUT,
output [1:0] HRESP,
output reg [AHB_DWIDTH-1:0] HRDATA,
output ahbsram_req,
output ahbsram_write,
output [AHB_AWIDTH-1:0] ahbsram_wdata,
output [2:0] ahbsram_size,
output [ADD_WIDTH-1:0] ahbsram_addr
);
//----------------------------------
// Local Parameter Declarations
//----------------------------------
// State Machine parameters
localparam IDLE = 2'b00;
localparam AHB_WR = 2'b01;
localparam AHB_RD = 2'b10;
parameter RESP_OKAY = 2'b00;
parameter RESP_ERROR = 2'b01;
// AHB HTRANS definition
parameter TRN_IDLE = 2'b00;
parameter TRN_BUSY = 2'b01;
parameter TRN_SEQ = 2'b11;
parameter TRN_NONSEQ = 2'b10;
parameter SINGLE = 3'b000;
parameter INCR = 3'b001;
parameter WRAP4 = 3'b010;
parameter INCR4 = 3'b011;
parameter WRAP8 = 3'b100;
parameter INCR8 = 3'b101;
parameter WRAP16 = 3'b110;
parameter INCR16 = 3'b111;
//----------------------------------
// Variable Declarations
//----------------------------------
reg [1:0] HTRANS_d;
reg [2:0] HBURST_d;
reg [2:0] HSIZE_d;
reg [ADD_WIDTH-1:0] HADDR_d;
reg [AHB_DWIDTH-1:0] HWDATA_d;
reg HWRITE_d;
reg HSEL_d;
reg HREADYIN_d;
reg [1:0] ahbcurr_state;
reg [1:0] ahbnext_state;
reg latchahbcmd;
reg ahbsram_req_int;
reg ahbsram_req_d1;
reg [AHB_DWIDTH-1:0] HWDATA_cal;
reg [4:0] burst_count;
reg [4:0] burst_count_reg;
reg [4:0] count;
wire aresetn;
wire sresetn;
//----------------------------------
// Start of Main Code
//----------------------------------
assign aresetn = (SYNC_RESET==1) ? 1'b1 : HRESETN;
assign sresetn = (SYNC_RESET==1) ? HRESETN : 1'b1;
// Generation of valid AHB Command which triggers the AHB Slave State Machine
assign validahbcmd = HREADYIN & HSEL & (HTRANS == TRN_NONSEQ);
// Generation of HRESP
assign HRESP = RESP_OKAY;
always @(*)
begin
HWDATA_cal = HWDATA;
end
// Latch all the AHB signals
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
HADDR_d <= {20{1'b0}};
HWDATA_d <= {32{1'b0}};
HTRANS_d <= 2'b00;
HSIZE_d <= 2'b00;
HBURST_d <= 3'b000;
HWRITE_d <= 1'b0;
HSEL_d <= 1'b0;
HREADYIN_d <= 1'b0;
end
else if (HREADYIN == 1'b1 & HSEL == 1'b1 & HREADYOUT == 1'b1) begin
HADDR_d <= HADDR;
HTRANS_d <= HTRANS;
HSIZE_d <= HSIZE;
HBURST_d <= HBURST;
HWRITE_d <= HWRITE;
HWDATA_d <= HWDATA_cal;
HSEL_d <= HSEL;
HREADYIN_d <= HREADYIN;
end
end
// Current State generation
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
ahbcurr_state <= IDLE;
end
else begin
ahbcurr_state <= ahbnext_state;
end
end
// Next State and output decoder logic
always @(*)
begin
latchahbcmd = 1'b0;
ahbsram_req_int = 1'b0;
ahbnext_state = ahbcurr_state;
case (ahbcurr_state)
IDLE : begin
if (HREADYIN == 1'b1 && HSEL == 1'b1 && ((HTRANS == TRN_NONSEQ) || HTRANS == TRN_SEQ)) begin
latchahbcmd = 1'b1;
if (HWRITE == 1'b1) begin
ahbnext_state = AHB_WR;
end
else begin
ahbnext_state = AHB_RD;
end
end
else begin
ahbnext_state = IDLE;
end
end
AHB_WR : begin
latchahbcmd = 1'b0;
ahbsram_req_int = 1'b1;
if (sramahb_ack == 1'b1) begin
if (count == burst_count_reg) begin
ahbnext_state = IDLE;
end
else begin
ahbsram_req_int = 1'b0;
end
end
end
AHB_RD : begin
latchahbcmd = 1'b0;
ahbsram_req_int = 1'b1;
if (sramahb_ack == 1'b1) begin
ahbnext_state = IDLE;
end
end
default : begin
ahbnext_state = IDLE;
end
endcase
end
// LOGIC FOR BURST COUNT
always @(*)
begin
burst_count = burst_count_reg;
if (HSEL == 1'b1 && HTRANS == TRN_NONSEQ && HREADYIN == 1'b1 && HREADYOUT == 1'b1) begin
case (HBURST)
SINGLE :
burst_count = 5'b00001;
WRAP4,INCR4 :
burst_count = 5'b00100;
WRAP8,INCR8 :
burst_count = 5'b01000;
WRAP16,INCR16 :
burst_count = 5'b10000;
default :
burst_count = 4'b0001;
endcase
end
end
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
burst_count_reg <= 'h0;
end
else begin
burst_count_reg <= burst_count;
end
end
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
count <= 5'h0;
end
else begin
if (count == burst_count_reg) begin
count <= 5'h0;
end
else if (ahbsram_req == 1'b1) begin
count <= count + 1'b1;
end
else begin
count <= count;
end
end
end
assign HREADYOUT = !ahbsram_req_int;
// Generation of signals required for SRAM
assign ahbsram_write = ahbsram_req ? HWRITE_d : 1'b0;
assign ahbsram_wdata = HWDATA;
assign ahbsram_addr = ahbsram_req ? HADDR_d : HADDR_d;
assign ahbsram_size = ahbsram_req ? HSIZE_d : HSIZE_d;
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
ahbsram_req_d1 <= 1'b0;
end
else begin
ahbsram_req_d1 <= ahbsram_req_int;
end
end
// Generate the request to the SRAM contol logic when there is AHB read or write request
assign ahbsram_req = ahbsram_req_int & !ahbsram_req_d1;
// HRDATA generation
always @(*)
begin
if (HREADYOUT && HREADYIN) begin
HRDATA = sramahb_rdata;
end
else begin
HRDATA = sramahb_rdata;
end
end
endmodule
//------------------------------------------------------------------------------
// File : sram_ctrl_if.v
// Editor : VSCode, Tab Size(4)
// Description :
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
module sram_ctrl_if #(
parameter AHB_DWIDTH = 32,
parameter ADD_WIDTH = 11,
parameter SYNC_RESET = 0
)
(
//----------------------------------
// IO Declarations
//----------------------------------
// Inputs
input HCLK,
input HRESETN,
input ahbsram_req,
input ahbsram_write,
input [2:0] ahbsram_size,
input [ADD_WIDTH-1:0] ahbsram_addr,
input [AHB_DWIDTH-1:0] ahbsram_wdata,
// Outputs
output sramahb_ack,
output reg [AHB_DWIDTH-1: 0] sramahb_rdata
);
//----------------------------------
// Local Parameter Declarations
//----------------------------------
// State Machine parameters
localparam S_IDLE = 2'b00;
localparam S_WR = 2'b01;
localparam S_RD = 2'b10;
//----------------------------------
// Variable Declarations
//----------------------------------
reg [3:0] sram_wen_mem;
reg [1:0] sramcurr_state;
reg [1:0] sramnext_state;
reg sram_wen;
reg sram_ren;
reg sramahb_ack_int;
reg sram_ren_d;
reg sram_done;
wire [AHB_DWIDTH-1:0] ram_rdata;
wire aresetn;
wire sresetn;
//----------------------------------
// Start of Main Code
//----------------------------------
assign aresetn = (SYNC_RESET == 1) ? 1'b1 : HRESETN;
assign sresetn = (SYNC_RESET == 1) ? HRESETN : 1'b1;
// Current State generation
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
sramcurr_state <= S_IDLE;
end
else begin
sramcurr_state <= sramnext_state;
end
end
// Next State and output decoder logic
always @(*)
begin
sramahb_ack_int = 1'b0;
sram_wen = 1'b0;
sram_ren = 1'b0;
sramnext_state = sramcurr_state;
case (sramcurr_state)
S_IDLE : begin
if (ahbsram_req == 1'b1) begin
if (ahbsram_write == 1'b1) begin
sramnext_state = S_WR;
sram_wen = 1'b1;
end
else begin
sram_ren = 1'b1;
sramnext_state = S_RD;
end
end
end
S_WR : begin
if (sram_done == 1'b1) begin
sramnext_state = S_IDLE;
sramahb_ack_int = 1'b1;
end
end
S_RD : begin
if (sram_done == 1'b1) begin
sramnext_state = S_IDLE;
sramahb_ack_int = 1'b1;
end
end
default : begin
sramnext_state = S_IDLE;
end
endcase
end
always @(*)
begin
sram_wen_mem = 4'b0000;
if (ahbsram_size == 3'b010) begin
sram_wen_mem = {4{sram_wen}};
end
else if (ahbsram_size == 3'b001) begin
case (ahbsram_addr[1])
1'b0 : begin
sram_wen_mem[0] = sram_wen;
sram_wen_mem[1] = sram_wen;
sram_wen_mem[2] = 1'b0;
sram_wen_mem[3] = 1'b0;
end
1'b1 : begin
sram_wen_mem[0] = 1'b0;
sram_wen_mem[1] = 1'b0;
sram_wen_mem[2] = sram_wen;
sram_wen_mem[3] = sram_wen;
end
endcase
end
else if (ahbsram_size == 3'b000) begin
case (ahbsram_addr[1:0])
2'b00 : begin
sram_wen_mem[0] = sram_wen;
sram_wen_mem[1] = 1'b0;
sram_wen_mem[2] = 1'b0;
sram_wen_mem[3] = 1'b0;
end
2'b01 : begin
sram_wen_mem[0] = 1'b0;
sram_wen_mem[1] = sram_wen;
sram_wen_mem[2] = 1'b0;
sram_wen_mem[3] = 1'b0;
end
2'b10 : begin
sram_wen_mem[0] = 1'b0;
sram_wen_mem[1] = 1'b0;
sram_wen_mem[2] = sram_wen;
sram_wen_mem[3] = 1'b0;
end
2'b11 : begin
sram_wen_mem[0] = 1'b0;
sram_wen_mem[1] = 1'b0;
sram_wen_mem[2] = 1'b0;
sram_wen_mem[3] = sram_wen;
end
endcase
end
else begin
sram_wen_mem = {4{sram_wen}};
end
end
// SRAM Instantiations
sram_model #(
.SYNC_RESET (SYNC_RESET),
.ADDR_WIDTH (ADD_WIDTH)
)
u_sram_model (
.writedata (ahbsram_wdata),
.readdata (ram_rdata[31:0]),
.wren (sram_wen_mem),
.rden (sram_ren),
.writeaddr (ahbsram_addr[ADD_WIDTH-1:2]),
.readaddr (ahbsram_addr[ADD_WIDTH-1:2]),
.clk (HCLK),
.resetn (HRESETN)
);
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
sramahb_rdata <= 32'h0;
end
else if (sram_ren_d == 1'b1) begin
sramahb_rdata <= ram_rdata;
end
else begin
sramahb_rdata <= sramahb_rdata;
end
end
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
sram_ren_d <= 32'h0;
end
else begin
sram_ren_d <= sram_ren;
end
end
// Generate the SRAM done when the SRAM wren/rden is done
always @(posedge HCLK or negedge aresetn)
begin
if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
sram_done <= 1'b0;
end
else if (sram_wen || sram_ren) begin
sram_done <= 1'b1;
end
else begin
sram_done <= 1'b0;
end
end
// Generate the SRAM ack
assign sramahb_ack = sramahb_ack_int;
endmodule
//------------------------------------------------------------------------------
// File : sram_model.v
// Editor : GVIM, Tab Size(4)
// Description : FPGA Block Ram/Onchip SRAM.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
module sram_model #(
parameter SYNC_RESET = 0,
parameter ADDR_WIDTH = 16
)
(
//----------------------------------
// IO Declarations
//----------------------------------
input [31:0] writedata,
output [31:0] readdata,
input [3:0] wren,
input rden,
input [ADDR_WIDTH-1:2] writeaddr,
input [ADDR_WIDTH-1:2] readaddr,
input clk,
input resetn
);
//----------------------------------
//--Local Parameter Declarations
//----------------------------------
localparam AWT = ((1<<(ADDR_WIDTH-2))-1);
//----------------------------------
// Variable Declarations
//----------------------------------
reg [7:0] bram0[AWT:0];
reg [7:0] bram1[AWT:0];
reg [7:0] bram2[AWT:0];
reg [7:0] bram3[AWT:0];
reg [ADDR_WIDTH-3:0] addr_q1;
reg rden_r = 1'b0;
wire [31:0] readdata_i;
wire aresetn;
wire sresetn;
//----------------------------------
// Start of Main Code
//----------------------------------
assign aresetn = (SYNC_RESET == 1) ? 1'b1 : resetn;
assign sresetn = (SYNC_RESET == 1) ? resetn : 1'b1;
always @(posedge clk)
begin
rden_r <= rden;
end
// Infer Block RAM
always @(posedge clk)
begin
if (wren[0])
bram0[writeaddr] <= writedata[7:0];
if (wren[1])
bram1[writeaddr] <= writedata[15:8];
if (wren[2])
bram2[writeaddr] <= writedata[23:16];
if (wren[3])
bram3[writeaddr] <= writedata[31:24];
end
always @(posedge clk)
begin
addr_q1 <= readaddr[ADDR_WIDTH-1:2];
end
assign readdata_i = {bram3[addr_q1],bram2[addr_q1],bram1[addr_q1],bram0[addr_q1]};
assign readdata = rden_r ? readdata_i : {32{1'b0}};
endmodule
下面是测试用例代码:
//------------------------------------------------------------------------------
// File : top_tb.v
// Editor : VSCode, Tab Size(4)
// Description : Testbench of AHB SRAM Controller.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
module top_tb();
//----------------------------------
// Local Parameter Declarations
//----------------------------------
localparam AHB_CLK_PERIOD = 5; // Assuming AHB CLK to be 100MHz
localparam SIZE_IN_BYTES =2048;
//----------------------------------
// Variable Declarations
//----------------------------------
reg HCLK = 0;
wire HWRITE;
wire [1:0] HTRANS;
wire [2:0] HSIZE;
wire [2:0] HBURST;
wire HREADYIN;
wire [31:0] HADDR;
wire [31:0] HWDATA;
wire HREADYOUT;
wire [1:0] HRESP;
wire [31:0] HRDATA;
reg HRESETn;
//----------------------------------
// Start of Main Code
//----------------------------------
//-----------------------------------------------------------------------
// Generate HCLK
//-----------------------------------------------------------------------
always #AHB_CLK_PERIOD
HCLK <= ~HCLK;
//-----------------------------------------------------------------------
// Generate HRESETn
//-----------------------------------------------------------------------
initial begin
HRESETn = 1'b0;
repeat(5) @(posedge HCLK);
HRESETn = 1'b1;
end
ahb_master #(
.START_ADDR (32'h0),
.DEPTH_IN_BYTES (SIZE_IN_BYTES)
)
u_ahb_master (
.HRESETn (HRESETn),
.HCLK (HCLK),
.HADDR (HADDR),
.HTRANS (HTRANS),
.HWRITE (HWRITE),
.HSIZE (HSIZE),
.HBURST (HBURST),
.HWDATA (HWDATA),
.HRDATA (HRDATA),
.HRESP (HRESP),
.HREADY (HREADYOUT)
);
ahb_sram # (
.AHB_AWIDTH (32),
.AHB_DWIDTH (32),
.SIZE_IN_BYTES (SIZE_IN_BYTES),
.SYNC_RESET (1)
)
u_ahb_sram (
.HCLK (HCLK),
.HRESETN (HRESETn),
.HSEL (1'b1),
.HWRITE (HWRITE),
.HADDR (HADDR),
.HWDATA (HWDATA),
.HRDATA (HRDATA),
.HSIZE (HSIZE),
.HTRANS (HTRANS),
.HBURST (HBURST),
.HRESP (HRESP),
.HREADYIN (1'b1),
.HREADYOUT (HREADYOUT)
);
`ifdef VCS
initial begin
$fsdbDumpfile("top_tb.fsdb");
$fsdbDumpvars;
end
initial begin
`ifdef DUMP_VPD
$vcdpluson();
`endif
end
`endif
endmodule
这个模块主要是产生了时钟和复位信号,例化了待测模块,以及一个AHB Master的功能模型。具体见下面的代码:
//------------------------------------------------------------------------------
// File : ahb_master.v
// Editor : VSCode, Tab Size(4)
// Description : AHB Master Simulation Model.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
`define SINGLE_TEST
`define BURST_TEST
module ahb_master #(
//----------------------------------
// Paramter Declarations
//----------------------------------
parameter START_ADDR = 0,
parameter DEPTH_IN_BYTES = 32'h100,
parameter END_ADDR = START_ADDR+DEPTH_IN_BYTES-1
)
(
//----------------------------------
// IO Declarations
//----------------------------------
input wire HRESETn,
input wire HCLK,
output reg [31:0] HADDR,
output reg [1:0] HTRANS,
output reg HWRITE,
output reg [2:0] HSIZE,
output reg [2:0] HBURST,
output reg [31:0] HWDATA,
input wire [31:0] HRDATA,
input wire [1:0] HRESP,
input wire HREADY
);
//----------------------------------
// Variable Declarations
//----------------------------------
reg [31:0] data_burst[0:1023];
//----------------------------------
// Start of Main Code
//----------------------------------
initial begin
HADDR = 0;
HTRANS = 0;
HWRITE = 0;
HSIZE = 0;
HBURST = 0;
HWDATA = 0;
while(HRESETn === 1'bx) @(posedge HCLK);
while(HRESETn === 1'b1) @(posedge HCLK);
while(HRESETn === 1'b0) @(posedge HCLK);
`ifdef SINGLE_TEST
repeat(3) @(posedge HCLK);
memory_test(START_ADDR, END_ADDR, 1);
memory_test(START_ADDR, END_ADDR, 2);
memory_test(START_ADDR, END_ADDR, 4);
`endif
`ifdef BURST_TEST
repeat(5) @(posedge HCLK);
memory_test_burst(START_ADDR, END_ADDR, 1);
memory_test_burst(START_ADDR, END_ADDR, 2);
memory_test_burst(START_ADDR, END_ADDR, 4);
memory_test_burst(START_ADDR, END_ADDR, 6);
memory_test_burst(START_ADDR, END_ADDR, 8);
memory_test_burst(START_ADDR, END_ADDR, 10);
memory_test_burst(START_ADDR, END_ADDR, 16);
memory_test_burst(START_ADDR, END_ADDR, 32);
memory_test_burst(START_ADDR, END_ADDR, 64);
memory_test_burst(START_ADDR, END_ADDR, 128);
memory_test_burst(START_ADDR, END_ADDR, 255);
repeat(5) @(posedge HCLK);
`endif
$finish(2);
end
//-----------------------------------------------------------------------
// Single transfer test
//-----------------------------------------------------------------------
task memory_test;
input [31:0] start; // start address
input [31:0] finish; // end address
input [2:0] size; // data size: 1, 2, 4
integer i;
integer error;
reg [31:0] data;
reg [31:0] gen;
reg [31:0] got;
reg [31:0] reposit[START_ADDR:END_ADDR];
begin
$display("%m: read-after-write test with %d-byte access", size);
error = 0;
gen = $random(7);
for (i = start; i < (finish-size+1); i = i + size) begin
gen = $random & ~32'b0;
data = align(i, gen, size);
ahb_write(i, size, data);
ahb_read(i, size, got);
got = align(i, got, size);
if (got !== data) begin
$display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data);
error = error + 1;
end
end
if (error == 0)
$display("[%10d] %m OK: from %x to %x", $time, start, finish);
$display("%m read-all-after-write-all with %d-byte access", size);
error = 0;
gen = $random(1);
for (i = start; i < (finish-size+1); i = i + size) begin
gen = {$random} & ~32'b0;
data = align(i, gen, size);
reposit[i] = data;
ahb_write(i, size, data);
end
for (i = start; i < (finish-size+1); i = i + size) begin
data = reposit[i];
ahb_read(i, size, got);
got = align(i, got, size);
if (got !== data) begin
$display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data);
error = error + 1;
end
end
if (error == 0)
$display("[%10d] %m OK: from %x to %x", $time, start, finish);
end
endtask
//-----------------------------------------------------------------------
// Burst transfer test
//-----------------------------------------------------------------------
task memory_test_burst;
input [31:0] start; // start address
input [31:0] finish; // end address
input [7:0] leng; // burst length
integer i;
integer j;
integer k;
integer r;
integer error;
reg [31:0] data;
reg [31:0] gen;
reg [31:0] got;
reg [31:0] reposit[0:1023];
integer seed;
begin
$display("%m: read-all-after-write-all burst test with %d-beat access", leng);
error = 0;
seed = 111;
gen = $random(seed);
k = 0;
if (finish > (start+leng*4)) begin
for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin
for (j = 0; j < leng; j = j + 1) begin
data_burst[j] = $random;
reposit[j+k*leng] = data_burst[j];
end
@(posedge HCLK);
ahb_write_burst(i, leng);
k = k + 1;
end
gen = $random(seed);
k = 0;
for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin
@(posedge HCLK);
ahb_read_burst(i, leng);
for (j = 0; j < leng; j = j + 1) begin
if (data_burst[j] != reposit[j+k*leng]) begin
error = error+1;
$display("%m A=%hh D=%hh, but %hh expected",i+j*leng, data_burst[j], reposit[j+k*leng]);
end
end
k = k + 1;
r = $random & 8'h0F;
repeat(r) @(posedge HCLK);
end
if (error == 0)
$display("%m %d-length burst read-after-write OK: from %hh to %hh",leng, start, finish);
end
else begin
$display("%m %d-length burst read-after-write from %hh to %hh ???",leng, start, finish);
end
end
endtask
//-----------------------------------------------------------------------
// As AMBA AHB bus uses non-justified data bus scheme, data should be
// aligned according to the address.
//-----------------------------------------------------------------------
function [31:0] align;
input [ 1:0] addr;
input [31:0] data;
input [ 2:0] size; // num of bytes
begin
`ifdef BIG_ENDIAN
case (size)
1 :
case (addr[1:0])
0 : align = data & 32'hFF00_0000;
1 : align = data & 32'h00FF_0000;
2 : align = data & 32'h0000_FF00;
3 : align = data & 32'h0000_00FF;
endcase
2 :
case (addr[1])
0 : align = data & 32'hFFFF_0000;
1 : align = data & 32'h0000_FFFF;
endcase
4 :
align = data&32'hFFFF_FFFF;
default :
$display($time,,"%m ERROR %d-byte not supported for size", size);
endcase
`else
case (size)
1 :
case (addr[1:0])
0 : align = data & 32'h0000_00FF;
1 : align = data & 32'h0000_FF00;
2 : align = data & 32'h00FF_0000;
3 : align = data & 32'hFF00_0000;
endcase
2 :
case (addr[1])
0 : align = data & 32'h0000_FFFF;
1 : align = data & 32'hFFFF_0000;
endcase
4 :
align = data&32'hFFFF_FFFF;
default :
$display($time,,"%m ERROR %d-byte not supported for size", size);
endcase
`endif
end
endfunction
`include "ahb_transaction_tasks.v"
endmodule
这个模块主要是实现了两个testcase,一个是测试single传输,另一个测试burst传输。AHB读写传输任务被封装在ahb_transaction_tasks.v文件中:
//------------------------------------------------------------------------------
// File : ahb_transaction_tasks.v
// Editor : VSCode, Tab Size(4)
// Description : AHB Transaction Tasks.
//
//------------------------------------------------------------------------------
`ifndef __AHB_TRANSACTION_TASKS_V__
`define __AHB_TRANSACTION_TASKS_V__
//-----------------------------------------------------------------------
// AHB Read Task
//-----------------------------------------------------------------------
task ahb_read;
input [31:0] address;
input [2:0] size;
output [31:0] data;
begin
@(posedge HCLK);
HADDR <= #1 address;
HTRANS <= #1 2'b10; // NONSEQ;
HBURST <= #1 3'b000; // SINGLE;
HWRITE <= #1 1'b0; // READ;
case (size)
1 : HSIZE <= #1 3'b000; // BYTE;
2 : HSIZE <= #1 3'b001; // HWORD;
4 : HSIZE <= #1 3'b010; // WORD;
default :
$display($time,, "ERROR: unsupported transfer size: %d-byte", size);
endcase
@(posedge HCLK);
while (HREADY !== 1'b1) @(posedge HCLK);
HTRANS <= #1 2'b0; // IDLE
@(posedge HCLK);
while (HREADY === 0) @(posedge HCLK);
data = HRDATA; // must be blocking
if (HRESP != 2'b00)
$display($time,, "ERROR: non OK response for read");
@(posedge HCLK);
end
endtask
//-----------------------------------------------------------------------
// AHB Write Task
//-----------------------------------------------------------------------
task ahb_write;
input [31:0] address;
input [2:0] size;
input [31:0] data;
begin
@(posedge HCLK);
HADDR <= #1 address;
HTRANS <= #1 2'b10; // NONSEQ
HBURST <= #1 3'b000; // SINGLE
HWRITE <= #1 1'b1; // WRITE
case (size)
1 : HSIZE <= #1 3'b000; // BYTE
2 : HSIZE <= #1 3'b001; // HWORD
4 : HSIZE <= #1 3'b010; // WORD
default :
$display($time,, "ERROR: unsupported transfer size: %d-byte", size);
endcase
@(posedge HCLK);
while (HREADY !== 1) @(posedge HCLK);
HWDATA <= #1 data;
HTRANS <= #1 2'b0; // IDLE
@(posedge HCLK);
while (HREADY === 0) @(posedge HCLK);
if (HRESP != 2'b00)
$display($time,, "ERROR: non OK response write");
@(posedge HCLK);
end
endtask
//-----------------------------------------------------------------------
// AHB Read Burst Task
//-----------------------------------------------------------------------
task ahb_read_burst;
input [31:0] addr;
input [31:0] leng;
integer i;
integer ln;
integer k;
begin
k = 0;
@(posedge HCLK);
HADDR <= #1 addr;
addr = addr + 4;
HTRANS <= #1 2'b10; // NONSEQ
if (leng >= 16) begin
HBURST <= #1 3'b111; // INCR16
ln = 16;
end
else if (leng >= 8) begin
HBURST <= #1 3'b101; // INCR8
ln = 8;
end
else if (leng >= 4) begin
HBURST <= #1 3'b011; // INCR4
ln = 4;
end
else begin
HBURST <= #1 3'b001; // INCR
ln = leng;
end
HWRITE <= #1 1'b0; // READ
HSIZE <= #1 3'b010; // WORD
@(posedge HCLK);
while (HREADY == 1'b0) @(posedge HCLK);
while (leng > 0) begin
for (i = 0; i < ln-1; i = i + 1) begin
HADDR <= #1 addr;
addr = addr + 4;
HTRANS <= #1 2'b11; // SEQ;
@(posedge HCLK);
while (HREADY == 1'b0) @(posedge HCLK);
data_burst[k%1024] <= HRDATA;
k = k + 1;
end
leng = leng - ln;
if (leng == 0) begin
HADDR <= #1 0;
HTRANS <= #1 0;
HBURST <= #1 0;
HWRITE <= #1 0;
HSIZE <= #1 0;
end
else begin
HADDR <= #1 addr;
addr = addr + 4;
HTRANS <= #1 2'b10; // NONSEQ
if (leng >= 16) begin
HBURST <= #1 3'b111; // INCR16
ln = 16;
end
else if (leng >= 8) begin
HBURST <= #1 3'b101; // INCR8
ln = 8;
end
else if (leng >= 4) begin
HBURST <= #1 3'b011; // INCR4
ln = 4;
end
else begin
HBURST <= #1 3'b001; // INCR1
ln = leng;
end
@(posedge HCLK);
while (HREADY == 0) @(posedge HCLK);
data_burst[k%1024] = HRDATA; // must be blocking
k = k + 1;
end
end
@(posedge HCLK);
while (HREADY == 0) @(posedge HCLK);
data_burst[k%1024] = HRDATA; // must be blocking
end
endtask
//-----------------------------------------------------------------------
// AHB Write Burst Task
// It takes suitable burst first and then incremental.
//-----------------------------------------------------------------------
task ahb_write_burst;
input [31:0] addr;
input [31:0] leng;
integer i;
integer j;
integer ln;
begin
j = 0;
ln = 0;
@(posedge HCLK);
while (leng > 0) begin
HADDR <= #1 addr;
addr = addr + 4;
HTRANS <= #1 2'b10; // NONSEQ
if (leng >= 16) begin
HBURST <= #1 3'b111; // INCR16
ln = 16;
end
else if (leng >= 8) begin
HBURST <= #1 3'b101; // INCR8
ln = 8;
end
else if (leng >= 4) begin
HBURST <= #1 3'b011; // INCR4
ln = 4;
end
else begin
HBURST <= #1 3'b001; // INCR
ln = leng;
end
HWRITE <= #1 1'b1; // WRITE
HSIZE <= #1 3'b010; // WORD
for (i = 0; i < ln-1; i = i + 1) begin
@(posedge HCLK);
while (HREADY == 1'b0) @(posedge HCLK);
HWDATA <= #1 data_burst[(j+i)%1024];
HADDR <= #1 addr;
addr = addr + 4;
HTRANS <= #1 2'b11; // SEQ;
while (HREADY == 1'b0) @(posedge HCLK);
end
@(posedge HCLK);
while (HREADY == 0) @(posedge HCLK);
HWDATA <= #1 data_burst[(j+i)%1024];
if (ln == leng) begin
HADDR <= #1 0;
HTRANS <= #1 0;
HBURST <= #1 0;
HWRITE <= #1 0;
HSIZE <= #1 0;
end
leng = leng - ln;
j = j + ln;
end
@(posedge HCLK);
while (HREADY == 0) @(posedge HCLK);
if (HRESP != 2'b00) begin // OKAY
$display($time,, "ERROR: non OK response write");
end
`ifdef DEBUG
$display($time,, "INFO: write(%x, %d, %x)", addr, size, data);
`endif
HWDATA <= #1 0;
@(posedge HCLK);
end
endtask
`endif
使用VCS仿真,打印信息如下:
整体仿真波形如下:
不喜欢用DVE的话,也可以选择用Verdi查看波形,这种比较简单的调试,DVE也足够用了。下面贴一张突发传输的细节图:
评论 (0)