summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axi_addr.v
blob: 8d8ac75ad3d686ea5a80ccf56e56a6edcfe4eefa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
////////////////////////////////////////////////////////////////////////////////
//
// Filename: 	axi_addr.v
// {{{
// Project:	WB2AXIPSP: bus bridges and other odds and ends
//
// Purpose:	The AXI (full) standard has some rather complicated addressing
//		modes, where the address can either be FIXED, INCRementing, or
//	even where it can WRAP around some boundary.  When in either INCR or
//	WRAP modes, the next address must always be aligned.  In WRAP mode,
//	the next address calculation needs to wrap around a given value, and
//	that value is dependent upon the burst size (i.e. bytes per beat) and
//	length (total numbers of beats).  Since this calculation can be
//	non-trivial, and since it needs to be done multiple times, the logic
//	below captures it for every time it might be needed.
//
// 20200918 - modified to accommodate (potential) AXI3 burst lengths
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2019-2024, Gisselquist Technology, LLC
// {{{
// This file is part of the WB2AXIP project.
//
// The WB2AXIP project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License").  You may not use this project,
// or this file, except in compliance with the License.  You may obtain a copy
// of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
// }}}
module	axi_addr #(
		// {{{
		parameter	AW = 32,
				DW = 32,
		// parameter [0:0]	OPT_AXI3 = 1'b0,
		localparam	LENB = 8
		// }}}
	) (
		// {{{
		input	wire	[AW-1:0]	i_last_addr,
		input	wire	[2:0]		i_size, // 1b, 2b, 4b, 8b, etc
		input	wire	[1:0]		i_burst, // fixed, incr, wrap, reserved
		input	wire	[LENB-1:0]	i_len,
		output	wire	[AW-1:0]	o_next_addr
		// }}}
	);

	// Parameter/register declarations
	// {{{
	localparam		DSZ = $clog2(DW)-3;
	localparam [1:0]	FIXED     = 2'b00;
	// localparam [1:0]	INCREMENT = 2'b01;
	// localparam [1:0]	WRAP      = 2'b10;
	localparam		IN_AW = (AW >= 12) ? 12 : AW;
	localparam [IN_AW-1:0]	ONE = 1;

	reg	[IN_AW-1:0]	wrap_mask, increment;
	reg	[IN_AW-1:0]	crossblk_addr, aligned_addr, unaligned_addr;
	// }}}

	// Address increment
	// {{{
	always @(*)
	if (DSZ == 0)
		increment = 1;
	else if (DSZ == 1)
		increment = (i_size[0]) ? 2 : 1;
	else if (DSZ == 2)
		increment = (i_size[1]) ? 4 : ((i_size[0]) ? 2 : 1);
	else if (DSZ == 3)
		case(i_size[1:0])
		2'b00: increment = 1;
		2'b01: increment = 2;
		2'b10: increment = 4;
		2'b11: increment = 8;
		endcase
	else
		increment = (1<<i_size);
	// }}}

	// wrap_mask
	// {{{
	// The wrap_mask is used to determine which bits remain stable across
	// the burst, and which are allowed to change.  It is only used during
	// wrapped addressing.
	always @(*)
	begin
		// Start with the default, minimum mask

		/*
		// Here's the original code.  It works, but it's
		// not economical (uses too many LUTs)
		//
		if (i_len[3:0] == 1)
			wrap_mask = (1<<(i_size+1));
		else if (i_len[3:0] == 3)
			wrap_mask = (1<<(i_size+2));
		else if (i_len[3:0] == 7)
			wrap_mask = (1<<(i_size+3));
		else if (i_len[3:0] == 15)
			wrap_mask = (1<<(i_size+4));
		wrap_mask = wrap_mask - 1;
		*/

		// Here's what we *want*
		//
		// wrap_mask[i_size:0] = -1;
		//
		// On the other hand, since we're already guaranteed that our
		// addresses are aligned, do we really care about
		// wrap_mask[i_size-1:0] ?

		// What we want:
		//
		// wrap_mask[i_size+3:i_size] |= i_len[3:0]
		//
		// We could simplify this to
		//
		// wrap_mask = wrap_mask | (i_len[3:0] << (i_size));
		// Verilator lint_off WIDTH
		if (DSZ < 2)
			wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[0]));
		else if (DSZ < 4)
			wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size[1:0]));
		else
			wrap_mask = ONE | ({{(IN_AW-4){1'b0}},i_len[3:0]} << (i_size));
		// Verilator lint_on  WIDTH
	end
	// }}}

	// unaligned_addr
	always @(*)
		unaligned_addr = i_last_addr[IN_AW-1:0] + increment[IN_AW-1:0];

	// aligned_addr
	// {{{
	always @(*)
	if (i_burst != FIXED)
	begin
		// Align subsequent beats in any burst
		// {{{
		aligned_addr = unaligned_addr;
		// We use the bus size here to simplify the logic
		// required in case the bus is smaller than the
		// maximum.  This depends upon AxSIZE being less than
		// $clog2(DATA_WIDTH/8).
		if (DSZ < 2)
		begin
			// {{{
			// Align any subsequent address
			if (i_size[0])
				aligned_addr[0] = 0;
			// }}}
		end else if (DSZ < 4)
		begin
			// {{{
			// Align any subsequent address
			case(i_size[1:0])
			2'b00:  aligned_addr = unaligned_addr;
			2'b01:  aligned_addr[  0] = 0;
			2'b10:  aligned_addr[(AW-1>1) ? 1 : (AW-1):0]= 0;
			2'b11:  aligned_addr[(AW-1>2) ? 2 : (AW-1):0]= 0;
			endcase
			// }}}
		end else begin
			// {{{
			// Align any subsequent address
			case(i_size)
			3'b001: aligned_addr[  0] = 0;
			3'b010: aligned_addr[(AW-1>1) ? 1 : (AW-1):0]=0;
			3'b011: aligned_addr[(AW-1>2) ? 2 : (AW-1):0]=0;
			3'b100: aligned_addr[(AW-1>3) ? 3 : (AW-1):0]=0;
			3'b101: aligned_addr[(AW-1>4) ? 4 : (AW-1):0]=0;
			3'b110: aligned_addr[(AW-1>5) ? 5 : (AW-1):0]=0;
			3'b111: aligned_addr[(AW-1>6) ? 6 : (AW-1):0]=0;
			default: aligned_addr = unaligned_addr;
			endcase
			// }}}
		end
		// }}}
	end else
		aligned_addr = i_last_addr[IN_AW-1:0];
	// }}}

	// crossblk_addr from aligned_addr, for WRAP addressing
	// {{{
	always @(*)
	if (i_burst[1])
	begin
		// WRAP!
		crossblk_addr[IN_AW-1:0] = (i_last_addr[IN_AW-1:0] & ~wrap_mask)
				| (aligned_addr & wrap_mask);
	end else
		crossblk_addr[IN_AW-1:0] = aligned_addr;
	// }}}

	// o_next_addr: Guarantee only the bottom 12 bits change
	// {{{
	// This is really a logic simplification.  AXI bursts aren't allowed
	// to cross 4kB boundaries.  Given that's the case, we don't have to
	// suffer from the propagation across all AW bits, and can limit any
	// address propagation to just the lower 12 bits
	generate if (AW > 12)
	begin : WIDE_ADDRESS
		assign	o_next_addr = { i_last_addr[AW-1:12],
						crossblk_addr[11:0] };
	end else begin : NARROW_ADDRESS
		assign	o_next_addr = crossblk_addr[AW-1:0];
	end endgenerate
	// }}}

	// Make Verilator happy
	// {{{
	// Verilator lint_off UNUSED
	wire	unused;
	assign	unused = (LENB <= 4) ? &{1'b0, i_len[0] }
				: &{ 1'b0, i_len[LENB-1:4], i_len[0] };
	// Verilator lint_on UNUSED
	// }}}
endmodule