URL
https://opencores.org/ocsvn/freeahb/freeahb/trunk
Subversion Repositories freeahb
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 8 to Rev 9
- ↔ Reverse comparison
Rev 8 → Rev 9
/freeahb/trunk/ahb_master.v
24,10 → 24,10
// |
// 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. |
// described in Verilog-1995 for max compatibility. |
// |
// Icarus Verilog: |
// iverilog -g2001 -Wall ahb_master.v |
// iverilog -g1995 -Wall ahb_master.v |
// <No Errors/Warnings> |
// |
// Verilator: |
127,40 → 127,29
output [31:0] o_addr; // Corresponding address is presented here. |
output o_dav; // Used as o_data valid indicator. |
|
//////////////////////////// |
// Local Constants |
//////////////////////////// |
wire [1:0] IDLE = 0; |
wire [1:0] BUSY = 1; |
wire [1:0] NONSEQ = 2; |
wire [1:0] SEQ = 3; |
wire [1:0] OKAY = 0; |
wire [1:0] ERROR = 1; |
wire [1:0] SPLIT = 2; |
wire [1:0] RETRY = 3; |
wire [2:0] SINGLE = 0; |
wire [2:0] INCR = 1; |
wire [2:0] WRAP4 = 2; |
wire [2:0] INCR4 = 3; |
wire [2:0] WRAP8 = 4; |
wire [2:0] INCR8 = 5; |
wire [2:0] WRAP16 = 6; |
wire [2:0] INCR16 = 7; |
|
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 [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 [DATA_WDT-1:0] hwdata [1:0]; |
172,73 → 161,38
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; |
|
///////////////////////// |
// Assigns |
///////////////////////// |
|
// Beat counter. |
wire [BEAT_WDT-1:0] beat_ctr_val = hburst == INCR ? beat_ctr : (beat_ctr - {{(BEAT_WDT-1){1'd0}}, rd_wr}); |
wire [4:0] burst_ctr_val = hburst == INCR ? burst_ctr : (burst_ctr - {4'd0, rd_wr}); |
wire spl_ret_cyc_1 = gnt[1] && !i_hready && (i_hresp == RETRY || i_hresp == SPLIT); |
wire rd_wr = i_rd || (i_wr && i_dav); |
wire cont = ~i_sof; |
wire [31:0] bt = htrans[0] != BUSY ? 32'd1 : 32'd0; |
|
// 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); |
|
// Inputs are valid only if there is a read or if there is a write with valid data. |
wire rd_wr = i_rd || (i_wr && i_dav); |
|
// 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); |
|
// 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]}; |
|
// UI must change only if this is 1. |
assign o_next = (i_hready && i_hgrant && !pend_split); |
|
// hbus request |
assign o_next = (i_hready && i_hgrant && !pend_split); |
assign o_hbusreq = hbusreq; |
assign o_data = odata; |
assign o_addr = oaddr; |
assign o_dav = odav; |
|
// Output ports. |
assign o_data = odata; |
assign o_addr = oaddr; |
assign o_dav = odav; |
always @ (i_wrap, i_min_len, i_size) |
addr_mask[-1] = get_address_mask (i_wrap, i_min_len, i_size); |
|
///////////////////////// |
// Grant tracker. |
///////////////////////// |
|
// Passes grant throughout the pipeline. |
always @ (posedge i_hclk or negedge i_hreset_n) |
begin |
if ( !i_hreset_n ) |
gnt <= 2'd0; |
else if ( spl_ret_cyc_1 ) |
gnt <= 2'd0; // A split retry cycle 1 will invalidate the pipeline. |
gnt <= 2'd0; |
else if ( i_hready ) |
gnt <= {gnt[0], i_hgrant}; |
end |
|
////////////////////////// |
// Bus request |
////////////////////////// |
|
always @ (posedge i_hclk or negedge i_hreset_n) |
begin |
// Request bus when doing reads/writes else do not request bus |
if ( !i_hreset_n ) |
hbusreq <= 1'd0; |
else |
245,15 → 199,10
hbusreq <= i_rd | i_wr; |
end |
|
////////////////////////////// |
// Address phase. Stage I. |
////////////////////////////// |
|
always @ (posedge i_hclk or negedge i_hreset_n) |
begin |
if ( !i_hreset_n ) |
begin |
// Signal IDLE on reset. |
htrans[0] <= IDLE; |
pend_split <= 1'd0; |
hwdata[0] <= {DATA_WDT{1'd0}}; |
265,7 → 214,7
beat_ctr <= {(BEAT_WDT){1'd0}}; |
min_len[0] <= {(BEAT_WDT){1'd0}}; |
end |
else if ( spl_ret_cyc_1 ) // Split retry cycle I |
else if ( spl_ret_cyc_1 ) |
begin |
htrans[0] <= IDLE; |
pend_split <= 1'd1; |
272,17 → 221,16
end |
else if ( i_hready && i_hgrant ) |
begin |
pend_split <= 1'd0; // Any pending split will be cleared |
pend_split <= 1'd0; |
|
if ( pend_split ) |
begin |
// If there's a pending split, perform a pipeline rollback |
|
|
{hwrite[0], hsize[0], addr_mask[0], wrap[0], min_len[0]} <= |
{hwrite[1], hsize[1], addr_mask[1], wrap[1], min_len[1]}; |
{hwrite[0], hsize[0], addr_mask[0], wrap[0], min_len[0], haddr[0]} <= |
{hwrite[1], hsize[1], addr_mask[1], wrap[1], min_len[1], haddr[1]}; |
|
hwdata[0] <= unshift(hwdata[1], haddr[1]); |
haddr[0] <= haddr[1]; |
{burst_ctr, hburst} <= compute_hburst ( |
beat, |
1'd1, |
301,7 → 249,7
{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. |
if ( !cont && !rd_wr ) |
begin:blk0 |
htrans[0] <= IDLE; |
end |
314,8 → 262,7
b1k_spec(haddr[0], i_size, addr_mask[0]) |
) |
begin:blk1 |
// We need to recompute the burst type here |
haddr[0] <= gen_addr ( haddr[0], {31'd0, rd_wr}, {29'd0, i_size}, cont, addr_mask[-1] ); |
haddr[0] <= gen_addr ( (~cont) ? i_addr : haddr[0], {31'd0, rd_wr}, {29'd0, i_size}, cont, addr_mask[-1] ); |
|
{burst_ctr, hburst} <= compute_hburst ( |
beat_ctr, |
323,7 → 270,7
rd_wr, |
i_size, |
addr_mask[-1], |
haddr[0], |
(~cont) ? i_addr : haddr[0], |
i_wrap, |
{{(32-BEAT_WDT){1'd0}}, i_min_len} |
); |
333,8 → 280,6
end |
else |
begin:blk2 |
// We are in a normal burst. No need to change HBURST. |
|
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; |
344,10 → 289,6
end |
end |
|
////////////////////////////// |
// HWDATA phase. Stage II. |
////////////////////////////// |
|
always @ (posedge i_hclk or negedge i_hreset_n) |
begin |
if ( !i_hreset_n ) |
369,10 → 310,6
end |
end |
|
//////////////////////////////// |
// HRDATA phase. Stage III. |
//////////////////////////////// |
|
always @ (posedge i_hclk or negedge i_hreset_n) |
begin |
if ( !i_hreset_n ) |
393,15 → 330,11
end |
end |
|
////////////////////////////// |
// Functions (Pure). |
/////////////////////////////// |
|
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. |
get_address_mask = {32{1'd0}}; |
|
if ( wrap == 0 ) |
begin |
411,9 → 344,7
begin:blk3 |
integer i; |
|
// If the bit of addr mask is set, propagate the calculation |
// else keep previous value. Works only for 1-hot values. |
for(i=0;i<BEAT_WDT;i++) |
for(i=0;i<BEAT_WDT;i=i+1) |
if ( len == 1 << i ) |
get_address_mask = (1 << ({29'd0, hsize} + i)) - 1; |
end |
423,7 → 354,7
function [DATA_WDT-1:0] shift ( input [DATA_WDT-1:0] data, input [31:0] addr ); |
reg [31:0] shamt; |
begin |
shamt = addr % (DATA_WDT/8); // Supported since RHS is constant. |
shamt = addr % (DATA_WDT/8); |
shift = data << shamt; |
end |
endfunction |
431,7 → 362,7
function [DATA_WDT-1:0] unshift ( input [DATA_WDT-1:0] data, input [31:0] addr ); |
reg [31:0] shamt; |
begin |
shamt = addr % (DATA_WDT/8); // Supported since RHS is constant |
shamt = addr % (DATA_WDT/8); |
unshift = data >> shamt; |
end |
endfunction |
455,13 → 386,11
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, 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[2:0] == INCR ) |
507,7 → 436,7
input [31:0] mask ); |
integer i; |
begin |
for(i=0;i<32;i++) |
for(i=0;i<32;i=i+1) |
begin |
if ( mask[i] ) |
gen_addr[i] = cont == 1'd0 ? addr[i] : |
518,13 → 447,11
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] ) |
b1k_spec = 1'd1; |
else |