URL
https://opencores.org/ocsvn/freeahb/freeahb/trunk
Subversion Repositories freeahb
Compare Revisions
- This comparison shows the changes necessary to convert path
/freeahb/trunk
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/LICENSE
0,0 → 1,21
MIT License |
|
Copyright (C) 2017 Revanth Kamaraj |
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
of this software and associated documentation files (the "Software"), to deal |
in the Software without restriction, including without limitation the rights |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
copies of the Software, and to permit persons to whom the Software is |
furnished to do so, subject to the following conditions: |
|
The above copyright notice and this permission notice shall be included in all |
copies or substantial portions of the Software. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
SOFTWARE. |
/README.md
0,0 → 1,5
## AHB Master |
|
Author: Revanth Kamaraj (revanth91kamaraj@gmail.com) |
|
This repository provides a fully compliant AHB 2.0 Master with SPLIT/RETRY support. Bursts are done using a combination of INCR16/INCR8/INCR4 and INCR. |
/ahb_master.v
0,0 → 1,454
//////////////////////////////////////////////////////////////////////////////* |
// |
// This RTL describes a generic AHB master with support for single and |
// burst transfers. Split/retry pipeline rollback is also supported. |
// The entire design is driven by a single clock i.e., AHB clock. A global |
// asynchronous active low reset is provided. |
// |
////////////////////////////////////////////////////////////////////////////// |
// |
// MIT License |
// |
// Copyright (C) 2017 Revanth Kamaraj |
// |
// Permission is hereby granted, free of charge, to any person obtaining a copy |
// of this software and associated documentation files (the "Software"), to deal |
// in the Software without restriction, including without limitation the rights |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
// copies of the Software, and to permit persons to whom the Software is |
// furnished to do so, subject to the following conditions: |
// |
// The above copyright notice and this permission notice shall be included in all |
// copies or substantial portions of the Software. |
// |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
// SOFTWARE. |
// |
///////////////////////////////////////////////////////////////////////////////// |
|
module ahb_master #(parameter DATA_WDT = 32, parameter BEAT_WDT = 32) ( |
|
//////////////////////// |
// AHB interface. |
//////////////////////// |
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, |
|
/////////////////////////////////////////////////////////////////////// |
// User interface. |
/////////////////////////////////////////////////////////////////////// |
// |
// NOTE: You can change UI signals at any time if the unit is IDLING. |
// |
// NOTE: o_next is a combinational signal. |
// |
// NOTE: When reset is released, the signals on the UI should represent an |
// IDLING state. From there onwards, you can change the UI at any time but once |
// you change it, further changes can be made only if o_next = 1. You could |
// perhaps connect o_next to read_enable of a FIFO. |
// |
// To go to IDLE, you must follow this... |
// To set the unit to IDLE mode, make |
// i_sof = 1, i_rd = 0 and [ i_wr = 0 (or) |
// i_wr = 1 and i_dav = 0 ] |
// on o_next = 1. As mentioned above, you change UI signals without having |
// o_next = 1 but once changed you must change them again only when o_next = 1. |
// |
// NOTE: The first UI request of a burst must have valid data provided in case |
// of a write. You CANNOT start a burst with the first UI request having |
// wr = 1 and dav = 0. |
// |
/////////////////////////////////////////////////////////////////////// |
// Limitations of the UI |
/////////////////////////////////////////////////////////////////////// |
// |
// 1. Start address should be aligned with i_size. |
// |
/////////////////////////////////////////////////////////////////////// |
|
// UI must change only if this is 1. |
output reg o_next, // External FIFO pop. |
|
// Data to write onto AHB. No need to shift. |
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. |
|
// Burst properties. Address should be aligned to i_size. |
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. |
// For wrap, this should be 2**. |
|
// SOF |
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. No INCR extension. |
|
// Data read from AHB. No need to shift. |
output reg[DATA_WDT-1:0] o_data, // Data got from AHB is presented here. |
output reg[31:0] o_addr, // Corresponding address is presented here. |
output reg o_dav // Used as o_data valid indicator. |
); |
|
//////////////////// |
// Localparams |
//////////////////// |
|
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; |
|
// Abbreviations. |
localparam D = DATA_WDT-1; |
localparam B = BEAT_WDT-1; |
|
////////////////// |
// Flip-flops. |
////////////////// |
|
reg [4:0] burst_ctr; // Small counter to keep track of current burst count. |
reg [B:0] beat_ctr; // Counter to keep track of word/beat count. |
|
// Pipeline flip-flops. |
reg [1:0] gnt; |
reg [2:0] hburst; // Only for stage 1. |
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; // Only for stage 2. |
|
// Tracks if we are in a pending state. |
reg pend_split; |
|
//////////////////////// |
// Signal aliases. |
///////////////////////// |
|
// Wire to check 1K crossing. |
reg b1k_spec; |
|
// 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); |
|
// Detects that 1k boundary condition will be crossed on next address |
always @* |
begin:blk4 |
logic [31:0] comp_addr; |
integer i; |
|
comp_addr = haddr[0]; |
|
for(i=0;i<32;i++) |
if ( addr_mask[i] ) |
comp_addr[i] = ((haddr[0] + (1 << i_size)) >> 10) >> i; |
|
if ( comp_addr != haddr[0][31:10] ) |
b1k_spec = 1'd1; |
else |
b1k_spec = 1'd0; |
end |
|
// 1 = Continues previous burst. Invert SOF. |
wire cont = ~i_sof; |
|
// Calculate address mask |
wire [31:0] addr_mask = get_address_mask (i_wrap, i_min_len, i_size); |
|
//////////////////// |
// Misc. logic. |
///////////////////// |
|
// 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. |
///////////////////////// |
|
// 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. |
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 ) |
o_hbusreq <= 1'd0; |
else |
o_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; |
end |
else if ( spl_ret_cyc_1 ) // Split retry cycle I |
begin |
htrans[0] <= IDLE; |
pend_split <= 1'd1; |
end |
else if ( i_hready && i_hgrant ) |
begin |
pend_split <= 1'd0; // Any pending split will be cleared |
|
if ( pend_split ) |
begin |
// If there's a pending split, perform a pipeline rollback |
|
{hwrite[0], hsize[0]} <= |
{hwrite[1], hsize[1]}; |
|
hwdata[0] <= unshift(hwdata[1], haddr[1]); |
haddr[0] <= haddr[1]; |
hburst <= compute_hburst (beat, haddr[1], hsize[1]); |
htrans[0] <= NONSEQ; |
burst_ctr <= compute_burst_ctr(beat, haddr[1], hsize[1]); |
beat_ctr <= beat; |
end |
else |
begin |
{hwdata[0], hwrite[0], hsize[0]} <= {i_data, i_wr, i_size}; |
|
if ( !cont && !rd_wr ) // Signal IDLE. |
begin |
htrans[0] <= IDLE; |
end |
else if ( (!cont && rd_wr) || !gnt[0] || (burst_ctr == 1 && o_hburst != INCR) |
|| htrans[0] == IDLE || b1k_spec ) |
begin:blk1 |
// We need to recompute the burst type here |
integer i; |
|
for(i=0;i<32;i++) |
begin |
if ( !cont ) |
haddr[0] <= i_addr; |
else if ( addr_mask[i] ) |
haddr[0][i] <= (haddr[0] + (rd_wr << i_size)) >> i; |
end |
|
hburst <= compute_hburst (!cont ? i_min_len : beat_ctr, |
!cont ? i_addr : haddr[0] + (rd_wr << i_size) , i_size); |
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); |
|
beat_ctr <= !cont ? i_min_len : ((hburst == INCR) ? beat_ctr : beat_ctr - rd_wr); |
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[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); |
end |
end |
end |
end |
|
////////////////////////////// |
// HWDATA phase. Stage II. |
////////////////////////////// |
|
always @ (posedge i_hclk) |
begin |
if ( i_hready && gnt[0] ) |
begin |
hwdata[1] <= shift(hwdata[0], haddr[0]); |
|
{haddr[1], hwrite[1], hsize[1], htrans[1], beat} <= |
{haddr[0], hwrite[0], hsize[0], htrans[0], beat_ctr}; |
end |
end |
|
//////////////////////////////// |
// HRDATA phase. Stage III. |
//////////////////////////////// |
|
always @ (posedge i_hclk or negedge i_hreset_n) |
begin |
if ( !i_hreset_n ) |
o_dav <= 1'd0; |
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]; |
end |
else |
o_dav <= 1'd0; |
end |
|
////////////////////////////// |
// Functions. |
/////////////////////////////// |
|
function [31:0] get_address_mask ( input wrap, input [BEAT_WDT-1:0] len, |
input [2:0] hsize ); |
begin |
get_address_mask = 32'hFFFF_FFFF; |
|
if ( wrap == 0 ) |
begin |
get_address_mask = 32'hFFFF_FFFF; |
end |
else |
begin:blk3 |
integer i; |
|
// If the bit of addr mask is set, propagate the calculation else |
// keep previous value. |
for(i=0;i<BEAT_WDT;i++) |
begin |
if ( len[i] ) |
begin |
get_address_mask = (1 << (hsize + i)) - 1; |
end |
end |
end |
end |
endfunction |
|
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. |
shift = data << shamt; |
end |
endfunction |
|
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 |
unshift = data >> shamt; |
end |
endfunction |
|
function [2:0] compute_hburst (input [B:0] val, input [31:0] addr, input [2:0] sz); |
begin |
compute_hburst = (val >= 16 && no_cross(addr, 15, sz)) ? INCR16 : |
(val >= 8 && no_cross(addr, 7, sz)) ? INCR8 : |
(val >= 4 && no_cross(addr, 3, sz)) ? INCR4 : INCR; |
|
// Wrap transfer active. |
if ( addr_mask != 32'hFFFF_FFFF ) |
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; |
end |
end |
endfunction |
|
function [4:0] compute_burst_ctr(input [B:0] val, input [31:0] addr, input [2:0] sz); |
begin |
compute_burst_ctr = (val >= 16 && no_cross(addr, 15, sz)) ? 5'd16 : |
(val >= 8 && no_cross(addr, 7, sz)) ? 5'd8 : |
(val >= 4 && no_cross(addr, 3, sz)) ? 5'd4 : 5'd0; |
end |
endfunction |
|
function no_cross(input [31:0] addr, input [31:0] val, input [2:0] sz); |
if ( addr + (val << (1 << sz )) >> 10 != addr[31:10] ) |
no_cross = 1'd0; // Crossed! |
else |
no_cross = 1'd1; // Not crossed |
endfunction |
|
endmodule |
|
/////////////////////////////////////////////////////////////////////////////// |
// EOF |
/////////////////////////////////////////////////////////////////////////////// |
|
/autocommit.sh
0,0 → 1,21
date=`date` |
|
git status |
git pull orgin master |
git status |
git add * |
git status |
git commit -am "Autosave $date" |
git status |
git push origin master |
git status |
|
svn status |
svn st | grep ^! | awk '{print " --force "$2}' | xargs svn rm |
svn status |
svn add --force * --auto-props --parents --depth infinity |
svn status |
svn commit -m "Autosave $date" |
svn status |
|
|