OpenCores
URL https://opencores.org/ocsvn/freeahb/freeahb/trunk

Subversion Repositories freeahb

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /freeahb
    from Rev 7 to Rev 8
    Reverse comparison

Rev 7 → Rev 8

/trunk/ahb_master.v
21,131 → 21,171
// SOFTWARE.
//
///////////////////////////////////////////////////////////////////////////////
//
// This is a Verilog AHB master. It passes lint in Icarus Verilog and Verilog.
// It is strictly compliant with the AHB 2.0 specification. The logic is
// described in Verilog-2001.
//
// Icarus Verilog:
// iverilog -g2001 -Wall ahb_master.v
// <No Errors/Warnings>
//
// Verilator:
// vlator --lint-only ahb_master.v
// <No Errors/Warnings>
//
///////////////////////////////////////////////////////////////////////////////
 
module ahb_master #(parameter DATA_WDT = 32, parameter BEAT_WDT = 32) (
module ahb_master (
i_hclk,
i_hreset_n,
o_haddr,
o_hburst,
o_htrans,
o_hwdata,
o_hwrite,
o_hsize,
i_hrdata,
i_hready,
i_hresp,
i_hgrant,
o_hbusreq,
o_next,
i_data,
i_dav,
i_addr,
i_size,
i_wr,
i_rd,
i_wrap,
i_min_len,
i_sof,
o_data,
o_addr,
o_dav
);
 
///////////////////////////////////////////////////////////////////////
// AHB interface.
///////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////
 
input i_hclk,
input i_hreset_n,
output reg [31:0] o_haddr,
output reg [2:0] o_hburst,
output reg [1:0] o_htrans,
output reg[DATA_WDT-1:0]o_hwdata,
output reg o_hwrite,
output reg [2:0] o_hsize,
input [DATA_WDT-1:0]i_hrdata,
input i_hready,
input [1:0] i_hresp,
input i_hgrant,
output reg o_hbusreq,
parameter DATA_WDT = 32;
parameter BEAT_WDT = 32;
 
///////////////////////////////////////////////////////////////////////
// User interface.
///////////////////////////////////////////////////////////////////////
// Required UI must change only if this is 1. You can also
// change UI signals when the unit idling (SOF=0, RD=0, WR=0).
///////////////////////////////////////////////////////////////////////
// AHB interface.
///////////////////////////////////////////////////////////////////////
 
output reg o_next, // External FIFO pop.
input i_hclk;
input i_hreset_n;
output [31:0] o_haddr;
output [2:0] o_hburst;
output [1:0] o_htrans;
output [DATA_WDT-1:0] o_hwdata;
output o_hwrite;
output [2:0] o_hsize;
input [DATA_WDT-1:0] i_hrdata;
input i_hready;
input [1:0] i_hresp;
input i_hgrant;
output o_hbusreq;
 
// Data to write onto AHB. No need to shift.
///////////////////////////////////////////////////////////////////////
// User interface.
///////////////////////////////////////////////////////////////////////
 
input [DATA_WDT-1:0] i_data, // Data to write. Can change during burst if o_next = 1.
input i_dav, // Data to write valid. Can change during burst if o_next = 1.
// Required UI must change only if this is 1. You can also
// change UI signals when the unit idling (SOF=0, RD=0, WR=0).
 
// Burst properties. Address should be aligned to i_size.
output o_next; // External FIFO pop.
 
input [31:0] i_addr, // Base address of burst. Hold constant throughout the burst.
input [2:0] i_size, // 2**n bytes/beat. Hold constant throughout the burst.
input i_wr, // Write to AHB bus. Hold constant throughout the burst.
input i_rd, // Read from AHB bus. Hold constant throughout the burst.
input i_wrap, // Enable wrap transfer. i_min_len should be 2**.
input [BEAT_WDT-1:0] i_min_len, // Minimum guaranteed length of burst (number of beats).
// Hold constant throughout the burst.Keep i_sof=0 to extend the
// burst beyond i_min_len.For wrap, this should be striclty 2**.
// Data to write onto AHB. No need to shift.
 
// SOF
input [DATA_WDT-1:0] i_data; // Data to write. Can change during burst if o_next = 1.
input i_dav; // Data to write valid. Can change during burst if o_next = 1.
 
input i_sof, // 1 = Start of burst. Always 1 for disjoint transfers.
// When i_sof=1, new burst parameters should be
// provided. For wrap transfers, should go to
// 1 once transfer is done (or) dav ==0 i.e.,
// no INCR extension is available for wrap, as expected.
// For a burst, this should be a pulse on the first
// beat. For a write transfer, i_dav == 1 when i_sof == 1.
// Burst properties. Address should be aligned to i_size.
 
// Data read from AHB. No need to shift.
input [31:0] i_addr; // Base address of burst. Hold constant throughout the burst.
input [2:0] i_size; // 2**n bytes/beat. Hold constant throughout the burst.
input i_wr; // Write to AHB bus. Hold constant throughout the burst.
input i_rd; // Read from AHB bus. Hold constant throughout the burst.
input i_wrap; // Enable wrap transfer. i_min_len should be 2**.
input [BEAT_WDT-1:0] i_min_len; // Minimum guaranteed length of burst (number of beats).
// Hold constant throughout the burst.Keep i_sof=0 to extend the
// burst beyond i_min_len.For wrap, this should be striclty 2**.
 
output reg[DATA_WDT-1:0] o_data, // Data got from AHB is presented here (read).
output reg[31:0] o_addr, // Corresponding address is presented here.
output reg o_dav // Used as o_data valid indicator.
);
// SOF
 
////////////////////
// Localparams
////////////////////
input i_sof; // 1 = Start of burst. Always 1 for disjoint transfers.
// When i_sof=1, new burst parameters should be
// provided.
 
localparam [1:0] IDLE = 0;
localparam [1:0] BUSY = 1;
localparam [1:0] NONSEQ = 2;
localparam [1:0] SEQ = 3;
localparam [1:0] OKAY = 0;
localparam [1:0] ERROR = 1;
localparam [1:0] SPLIT = 2;
localparam [1:0] RETRY = 3;
localparam [2:0] SINGLE = 0;
localparam [2:0] INCR = 1;
localparam [2:0] WRAP4 = 2;
localparam [2:0] INCR4 = 3;
localparam [2:0] WRAP8 = 4;
localparam [2:0] INCR8 = 5;
localparam [2:0] WRAP16 = 6;
localparam [2:0] INCR16 = 7;
localparam [2:0] BYTE = 0;
localparam [2:0] HWORD = 1;
localparam [2:0] WORD = 2; // 32-bit
localparam [2:0] DWORD = 3; // 64-bit
localparam [2:0] BIT128 = 4;
localparam [2:0] BIT256 = 5;
localparam [2:0] BIT512 = 6;
localparam [2:0] BIT1024 = 7;
// Data read from AHB. No need to shift.
 
// Abbreviations.
localparam D = DATA_WDT-1;
localparam B = BEAT_WDT-1;
output [DATA_WDT-1:0] o_data; // Data got from AHB is presented here (read).
output [31:0] o_addr; // Corresponding address is presented here.
output o_dav; // Used as o_data valid indicator.
 
//////////////////
////////////////////////////
// Local Constants
////////////////////////////
 
localparam [1:0] IDLE = 0;
localparam [1:0] BUSY = 1;
localparam [1:0] NONSEQ = 2;
localparam [1:0] SEQ = 3;
localparam [1:0] OKAY = 0;
localparam [1:0] ERROR = 1;
localparam [1:0] SPLIT = 2;
localparam [1:0] RETRY = 3;
localparam [2:0] SINGLE = 0;
localparam [2:0] INCR = 1;
localparam [2:0] WRAP4 = 2;
localparam [2:0] INCR4 = 3;
localparam [2:0] WRAP8 = 4;
localparam [2:0] INCR8 = 5;
localparam [2:0] WRAP16 = 6;
localparam [2:0] INCR16 = 7;
 
///////////////////////////
// Flip-flops.
//////////////////
///////////////////////////
 
reg [4:0] burst_ctr;
reg [B:0] beat_ctr;
reg [DATA_WDT-1:0] odata;
reg [31:0] oaddr;
reg odav;
 
reg [4:0] burst_ctr;
reg [BEAT_WDT-1:0] beat_ctr;
reg hbusreq;
 
// Pipeline flip-flops.
reg [1:0] gnt;
reg [2:0] hburst;
reg [D:0] hwdata [1:0];
reg [31:0] haddr [1:0];
reg [1:0] htrans [1:0];
reg [1:0] hwrite;
reg [2:0] hsize [1:0];
reg [B:0] beat;
reg [31:0] addr_mask [1:-1];
reg [1:0] wrap;
reg [1:0] gnt;
reg [2:0] hburst;
reg [DATA_WDT-1:0] hwdata [1:0];
reg [31:0] haddr [1:0];
reg [1:0] htrans [1:0];
reg [1:0] hwrite;
reg [2:0] hsize [1:0];
reg [BEAT_WDT-1:0] min_len [1:0];
reg [BEAT_WDT-1:0] beat;
reg [31:0] addr_mask [1:-1];
reg [1:0] wrap;
 
// Tracks if we are in a pending state.
reg pend_split;
reg pend_split;
 
////////////////////////
// Signal aliases.
/////////////////////////
// Assigns
/////////////////////////
 
// Wire to check 1K crossing.
reg b1k_spec;
// Beat counter.
wire [BEAT_WDT-1:0] beat_ctr_val = hburst == INCR ? beat_ctr : (beat_ctr - {{(BEAT_WDT-1){1'd0}}, rd_wr});
 
// Burst counter.
wire [4:0] burst_ctr_val = hburst == INCR ? burst_ctr : (burst_ctr - {4'd0, rd_wr});
 
// Detects the first cycle of split and retry.
wire spl_ret_cyc_1 = gnt[1] && !i_hready && (i_hresp == RETRY || i_hresp == SPLIT);
 
155,41 → 195,29
// 1 = Continues previous burst. Invert SOF. 0 - New transfer.
wire cont = ~i_sof;
 
// Busy handling
wire [31:0] bt = htrans[0] != BUSY ? 32'd1 : 32'd0;
 
// Calculate address mask
always @* addr_mask[-1] = get_address_mask (i_wrap, i_min_len, i_size);
 
// Detects that 1k boundary condition will be crossed on next address
always @*
begin:blk4
logic [31:0] comp_addr;
integer i;
addr_mask[-1] = get_address_mask (i_wrap, i_min_len, i_size);
 
comp_addr = haddr[0];
// Output drivers.
assign {o_haddr, o_hburst, o_htrans, o_hwdata, o_hwrite, o_hsize} =
{haddr[0], hburst, htrans[0], hwdata[1], hwrite[0], hsize[0]};
 
// Address mask.
for(i=0;i<32;i++)
if ( addr_mask[i] )
comp_addr[i] = ((haddr[0] + (1 << i_size)) >> 10) >> i;
// UI must change only if this is 1.
assign o_next = (i_hready && i_hgrant && !pend_split);
 
// Check for 1K crossing.
if ( comp_addr[31:10] != haddr[0][31:10] )
b1k_spec = 1'd1;
else
b1k_spec = 1'd0;
end
// hbus request
assign o_hbusreq = hbusreq;
 
////////////////////
// Misc. logic.
/////////////////////
// Output ports.
assign o_data = odata;
assign o_addr = oaddr;
assign o_dav = odav;
 
// Output drivers.
always @* {o_haddr, o_hburst, o_htrans, o_hwdata, o_hwrite, o_hsize} =
{haddr[0], hburst, htrans[0], hwdata[1], hwrite[0], hsize[0]};
 
// UI must change only if this is 1.
always @* o_next = (i_hready && i_hgrant && !pend_split);
 
////////////////////////
/////////////////////////
// Grant tracker.
/////////////////////////
 
212,9 → 240,9
begin
// Request bus when doing reads/writes else do not request bus
if ( !i_hreset_n )
o_hbusreq <= 1'd0;
hbusreq <= 1'd0;
else
o_hbusreq <= i_rd | i_wr;
hbusreq <= i_rd | i_wr;
end
 
//////////////////////////////
226,12 → 254,20
if ( !i_hreset_n )
begin
// Signal IDLE on reset.
htrans[0] <= IDLE;
pend_split <= 1'd0;
htrans[0] <= IDLE;
pend_split <= 1'd0;
hwdata[0] <= {DATA_WDT{1'd0}};
hwrite[0] <= 1'd0;
addr_mask[0] <= 32'd0;
hsize[0] <= 3'd0;
wrap[0] <= 1'd0;
burst_ctr <= 5'd0;
beat_ctr <= {(BEAT_WDT){1'd0}};
min_len[0] <= {(BEAT_WDT){1'd0}};
end
else if ( spl_ret_cyc_1 ) // Split retry cycle I
begin
htrans[0] <= IDLE;
htrans[0] <= IDLE;
pend_split <= 1'd1;
end
else if ( i_hready && i_hgrant )
242,70 → 278,67
begin
// If there's a pending split, perform a pipeline rollback
 
{hwrite[0], hsize[0], addr_mask[0], wrap[0]} <=
{hwrite[1], hsize[1], addr_mask[1], wrap[1]};
{hwrite[0], hsize[0], addr_mask[0], wrap[0], min_len[0]} <=
{hwrite[1], hsize[1], addr_mask[1], wrap[1], min_len[1]};
 
hwdata[0] <= unshift(hwdata[1], haddr[1]);
haddr[0] <= haddr[1];
hburst <= compute_hburst(beat, haddr[1], hsize[1], addr_mask[1], haddr[1], wrap[1]);
{burst_ctr, hburst} <= compute_hburst (
beat,
1'd1,
1'd0,
hsize[1],
addr_mask[1],
haddr[1],
wrap[1],
{{(32-BEAT_WDT){1'd0}}, min_len[1]}
);
htrans[0] <= NONSEQ;
burst_ctr <= compute_burst_ctr(beat, haddr[1], hsize[1], addr_mask[1], haddr[1]);
beat_ctr <= beat;
end
else
begin
{hwdata[0], hwrite[0], hsize[0], addr_mask[0] , wrap[0]} <=
{i_data, i_wr, i_size , addr_mask[-1] , i_wrap };
{hwdata[0], hwrite[0], hsize[0], addr_mask[0] , wrap[0], min_len[0]} <=
{i_data, i_wr, i_size , addr_mask[-1] , i_wrap , i_min_len };
 
if ( !cont && !rd_wr ) // Signal IDLE.
begin
begin:blk0
htrans[0] <= IDLE;
end
else if ( (!cont && rd_wr) || !gnt[0] ||
(burst_ctr == 1 && o_hburst != INCR)
|| htrans[0] == IDLE || b1k_spec )
else if
(
(!cont && rd_wr) ||
!gnt[0] ||
(burst_ctr == 1 && o_hburst != INCR) ||
(htrans[0] == IDLE) ||
b1k_spec(haddr[0], i_size, addr_mask[0])
)
begin:blk1
// We need to recompute the burst type here
integer i;
haddr[0] <= gen_addr ( haddr[0], {31'd0, rd_wr}, {29'd0, i_size}, cont, addr_mask[-1] );
 
for(i=0;i<32;i++)
begin
if ( !cont )
haddr[0] <= i_addr;
else if ( addr_mask[-1][i] )
haddr[0][i] <=
(haddr[0] + (rd_wr << i_size)) >> i;
end
{burst_ctr, hburst} <= compute_hburst (
beat_ctr,
cont,
rd_wr,
i_size,
addr_mask[-1],
haddr[0],
i_wrap,
{{(32-BEAT_WDT){1'd0}}, i_min_len}
);
 
hburst <= compute_hburst (!cont ? i_min_len : beat_ctr,
!cont ? i_addr :
haddr[0] + (rd_wr << i_size) ,
i_size, addr_mask[-1], haddr[0],
i_wrap);
 
htrans[0] <= rd_wr ? NONSEQ : IDLE;
 
burst_ctr <= compute_burst_ctr(!cont ? i_min_len : beat_ctr - rd_wr,
!cont ? i_addr :
haddr[0] + (rd_wr << i_size) ,
i_size, addr_mask[-1], haddr[0]);
 
beat_ctr <= !cont ? i_min_len : ((hburst == INCR) ? beat_ctr : beat_ctr - rd_wr);
htrans[0] <= rd_wr == 1'd1 ? NONSEQ : IDLE;
beat_ctr <= cont == 1'd0 ? i_min_len : beat_ctr_val;
end
else
begin:blk2
// We are in a normal burst. No need to change HBURST.
 
integer i;
 
for(i=0;i<32;i++)
if ( addr_mask[-1][i] )
haddr[0][i] <=
(haddr[0] + ((htrans[0] != BUSY) << i_size)) >> i;
 
htrans[0] <= rd_wr ? SEQ : BUSY;
burst_ctr <= o_hburst == INCR ? burst_ctr : (burst_ctr - rd_wr);
beat_ctr <= o_hburst == INCR ? beat_ctr : (beat_ctr - rd_wr);
haddr[0] <= gen_addr(haddr[0], bt, {29'd0, i_size}, 1'd1, addr_mask[-1]);
htrans[0] <= rd_wr == 1'd1 ? SEQ : BUSY;
burst_ctr <= burst_ctr_val;
beat_ctr <= beat_ctr_val;
end
end
end
315,9 → 348,19
// HWDATA phase. Stage II.
//////////////////////////////
 
always @ (posedge i_hclk)
always @ (posedge i_hclk or negedge i_hreset_n)
begin
if ( i_hready && gnt[0] )
if ( !i_hreset_n )
begin
hwdata[1] <= {DATA_WDT{1'd0}};
haddr[1] <= 32'd0;
hwrite[1] <= 1'd0;
htrans[1] <= IDLE;
addr_mask[1] <= 32'd0;
hsize[1] <= 3'd0;
beat <= {(BEAT_WDT){1'd0}};
end
else if ( i_hready && gnt[0] )
begin
hwdata[1] <= shift(hwdata[0], haddr[0]);
 
333,22 → 376,29
always @ (posedge i_hclk or negedge i_hreset_n)
begin
if ( !i_hreset_n )
o_dav <= 1'd0;
begin
odav <= 1'd0;
odata <= {DATA_WDT{1'd0}};
oaddr <= 32'd0;
end
else if ( gnt[1] && i_hready && (htrans[1] == SEQ || htrans[1] == NONSEQ) )
begin
o_dav <= !hwrite[1];
o_data <= unshift(i_hrdata, haddr[1]);
o_addr <= haddr[1];
odav <= ~hwrite[1];
odata <= unshift(i_hrdata, haddr[1]);
oaddr <= haddr[1];
end
else
o_dav <= 1'd0;
begin
odav <= 1'd0;
end
end
 
//////////////////////////////
// Functions.
// Functions (Pure).
///////////////////////////////
 
function [31:0] get_address_mask ( input wrap, input [BEAT_WDT-1:0] len,
function [31:0] get_address_mask ( input wrap,
input [BEAT_WDT-1:0] len,
input [2:0] hsize );
begin
get_address_mask = {32{1'dx}}; // Synthesis optimize on no match.
386,75 → 436,99
end
endfunction
 
function [2:0] compute_hburst (input [B:0] val, input [31:0] addr,
input [2:0] sz, input [31:0] mask,
input [31:0] prev_addr,
input wrap);
function [7:0] compute_hburst
(
input [BEAT_WDT-1:0] beat,
input cont,
input rd_wr,
input [2:0] sz,
input [31:0] mask,
input [31:0] prev_addr,
input wrap,
input [31:0] min_len
);
reg [31:0] addr;
reg [BEAT_WDT-1:0] val;
begin:blk5
integer i;
 
// Mask address.
for(i=0;i<32;i++)
if ( mask[i] == 1'd0 )
addr[i] = prev_addr[i];
val = cont == 1'd0 ? min_len : beat;
addr = gen_addr(prev_addr, {31'd0, rd_wr}, {29'd0, sz}, cont, mask);
 
// Decide type of burst, assume no wrap.
compute_hburst = (val >= 16 && no_cross(addr, 15, sz, mask)) ? INCR16 :
(val >= 8 && no_cross(addr, 7, sz, mask)) ? INCR8 :
(val >= 4 && no_cross(addr, 3, sz, mask)) ? INCR4 :
INCR ;
compute_hburst = (val >= 16 && no_cross(addr, 32'd15, sz, mask)) == 1'd1 ? {5'd16, INCR16} :
(val >= 8 && no_cross(addr, 32'd7, sz, mask)) == 1'd1 ? {5'd8 , INCR8 } :
(val >= 4 && no_cross(addr, 32'd3, sz, mask)) == 1'd1 ? {5'd4 , INCR4 } :
{5'd0 , INCR } ;
 
// Now, if wrap transfer active, furthe refine the results.
if ( wrap == 1'd1 )
begin
if ( compute_hburst == INCR )
compute_hburst = SINGLE;
else if ( i_min_len == 4 )
compute_hburst = WRAP4;
else if ( i_min_len == 8 )
compute_hburst = WRAP8;
else if ( i_min_len == 16 )
compute_hburst = WRAP16;
else
compute_hburst = SINGLE;
if ( compute_hburst[2:0] == INCR )
compute_hburst[2:0] = SINGLE;
else
begin
case ( min_len )
32'd4 : compute_hburst[2:0] = WRAP4;
32'd8 : compute_hburst[2:0] = WRAP8;
32'd16 : compute_hburst[2:0] = WRAP16;
default : compute_hburst[2:0] = SINGLE;
endcase
end
end
end
endfunction
 
function [4:0] compute_burst_ctr(input [B:0] val, input [31:0] addr,
input [2:0] sz, input [31:0] mask,
input [31:0] prev_addr);
begin:blk6
integer i;
function no_cross
(
input [31:0] addr,
input [31:0] val,
input [2:0] sz,
input [31:0] mask
);
reg [31:0] comp_addr;
integer i;
begin
comp_addr = gen_addr(addr, val, 32'd1 << sz, 1'd1, mask );
 
// Mask address.
for(i=0;i<32;i++)
if ( mask[i] == 1'd0 )
addr[i] = prev_addr[i];
 
compute_burst_ctr = (val >= 16 && no_cross(addr, 15, sz, mask)) ? 5'd16 :
(val >= 8 && no_cross(addr, 7, sz, mask)) ? 5'd8 :
(val >= 4 && no_cross(addr, 3, sz, mask)) ? 5'd4 :
5'd0;
if ( comp_addr[31:10] != addr[31:10] )
no_cross = 1'd0;
else
no_cross = 1'd1;
end
endfunction
 
function no_cross (input [31:0] addr, input [31:0] val,
input [2:0] sz, input [31:0] mask);
reg [31:0] comp_addr;
function get_bit ( input [31:0] data, integer sel );
get_bit = data[sel];
endfunction
 
function [31:0] gen_addr ( input [31:0] addr, input [31:0] rd_wr,
input [31:0] sz, input cont,
input [31:0] mask );
integer i;
begin
comp_addr = addr;
 
// Override those with mask.
for(i=0;i<32;i++)
begin
if ( mask[i] )
comp_addr = addr + (val << (1 << sz));
gen_addr[i] = cont == 1'd0 ? addr[i] :
get_bit(addr + (rd_wr << sz), i);
else
gen_addr[i] = addr[i];
end
end
endfunction
 
// Detects that 1k boundary condition will be crossed on next address.
function b1k_spec ( input [31:0] addr, input [2:0] size, input [31:0] mask );
reg [31:0] comp_addr;
begin:blk4
comp_addr = gen_addr ( addr, 32'd1, {29'd0, size}, 1'd1, mask );
 
// Check for 1K crossing.
if ( comp_addr[31:10] != addr[31:10] )
no_cross = 1'd0;
b1k_spec = 1'd1;
else
no_cross = 1'd1;
b1k_spec = 1'd0;
end
endfunction
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.