1 //
2 // Copyright (c) 2010-2025 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 #include "axi-slave.h"
8 #include <cmath>
9 #include <cinttypes>
10 #include <cstdio>
11
AxiSlave(uint32_t dataWidth,uint32_t addrWidth)12 AxiSlave::AxiSlave(uint32_t dataWidth, uint32_t addrWidth) : BaseAxi(dataWidth, addrWidth)
13 {
14 writeState = AxiWriteState::AW;
15 readState = AxiReadState::AR;
16
17 arready_new = 0;
18 rvalid_new = 0;
19 rlast_new = 0;
20 rdata_new = 0;
21
22 awready_new = 0;
23 wready_new = 0;
24 bvalid_new = 0;
25 }
26
tick(bool countEnable,uint64_t steps=1)27 void AxiSlave::tick(bool countEnable, uint64_t steps = 1)
28 {
29 for(uint64_t i = 0; i < steps; i++) {
30 readHandler();
31 writeHandler();
32 *aclk = 1;
33 evaluateModel();
34 updateSignals();
35 *aclk = 0;
36 evaluateModel();
37 }
38
39 // Since we can run out of steps during an AXI transaction we must let
40 // the AXI master know that we can't accept more data at the moment.
41 // To do that we set all handshake signals to 0 and readHandler/writeHandler
42 // will handle resuming the transaction once tick is called again.
43 clearSignals();
44
45 if(countEnable) {
46 tickCounter += steps;
47 }
48 }
49
timeoutTick(uint8_t * signal,uint8_t expectedValue,int timeout=DEFAULT_TIMEOUT)50 void AxiSlave::timeoutTick(uint8_t* signal, uint8_t expectedValue, int timeout = DEFAULT_TIMEOUT)
51 {
52 do
53 {
54 tick(true);
55 timeout--;
56 }
57 while((*signal != expectedValue) && timeout > 0);
58
59 if(timeout == 0) {
60 throw "Operation timeout";
61 }
62 }
63
64 // Clear signals when leaving tick
65
clearSignals()66 void AxiSlave::clearSignals()
67 {
68 arready_new = *arready;
69 rvalid_new = *rvalid;
70 rdata_new = *rdata;
71 awready_new = *awready;
72 wready_new = *wready;
73 bvalid_new = *bvalid;
74
75 // Read
76 *arready = 0;
77 *rvalid = 0;
78 *rdata = 0;
79 // Write
80 *awready = 0;
81 *wready = 0;
82 *bvalid = 0;
83 }
84
85 // Update signals after rising edge
86
updateSignals()87 void AxiSlave::updateSignals()
88 {
89 // Read
90 *arready = arready_new;
91 *rvalid = rvalid_new;
92 *rlast = rlast_new;
93 *rdata = rdata_new;
94 // Write
95 *awready = awready_new;
96 *wready = wready_new;
97 *bvalid = bvalid_new;
98 }
99
100 // Sample signals before rising edge in handlers
101
readWord(uint64_t addr,uint8_t sel=0)102 void AxiSlave::readWord(uint64_t addr, uint8_t sel = 0)
103 {
104 sprintf(buffer, "Axi read from: 0x%" PRIX64, addr);
105 this->agent->log(0, buffer);
106 rdata_new = this->agent->requestDoubleWordFromAgent(addr);
107 }
108
readHandler()109 void AxiSlave::readHandler()
110 {
111 switch(readState) {
112 case AxiReadState::AR:
113 arready_new = 1;
114 if(*arready == 1 && *arvalid == 1) {
115 arready_new = 0;
116
117 readLen = *arlen;
118 readNumBytes = pow(2, *arsize);
119 readState = AxiReadState::R;
120 readBurstType = static_cast<AxiBurstType>(*arburst);
121 readAddr = uint64_t((*araddr)/readNumBytes) * readNumBytes;
122
123 rlast_new = (readLen == 0);
124
125 if(readAddr != *araddr)
126 throw "Unaligned transfers are not supported";
127
128 if(readBurstType != AxiBurstType::INCR)
129 throw "Unsupported AXI read burst type";
130
131 if(readNumBytes != int(dataWidth/8))
132 throw "Narrow bursts are not supported";
133
134 this->agent->log(0, "Axi read start");
135
136 readWord(readAddr);
137 }
138 break;
139 case AxiReadState::R:
140 rvalid_new = 1;
141
142 if(*rready == 1 && *rvalid == 1) {
143 if(readLen == 0) {
144 readState = AxiReadState::AR;
145 rvalid_new = 0;
146 rlast_new = 0;
147 this->agent->log(0, "Axi read transfer completed");
148 } else {
149 readLen--;
150 readAddr += int(dataWidth/8); // TODO: make data width configurable
151 readWord(readAddr);
152 rlast_new = (readLen == 0);
153 }
154 }
155 break;
156 default:
157 readState = AxiReadState::AR;
158 break;
159 }
160 }
161
writeWord(uint64_t addr,uint64_t data,uint8_t strb)162 void AxiSlave::writeWord(uint64_t addr, uint64_t data, uint8_t strb)
163 {
164 sprintf(buffer, "Axi write to: 0x%" PRIX64 ", data: 0x%" PRIX64 "", addr, data);
165 this->agent->log(0, buffer);
166 this->agent->pushDoubleWordToAgent(writeAddr, *wdata);
167 }
168
writeHandler()169 void AxiSlave::writeHandler()
170 {
171 switch(writeState) {
172 case AxiWriteState::AW:
173 awready_new = 1;
174 if(*awready == 1 && *awvalid == 1) {
175 awready_new = 0;
176
177 writeNumBytes = pow(2, *awsize);
178 writeState = AxiWriteState::W;
179 writeBurstType = static_cast<AxiBurstType>(*awburst);
180 writeAddr = uint64_t((*awaddr)/writeNumBytes) * writeNumBytes;
181
182 if(writeAddr != *awaddr)
183 throw "Unaligned transfers are not supported";
184
185 if(writeBurstType != AxiBurstType::INCR)
186 throw "Unsupported AXI write burst type";
187
188 if(writeNumBytes != int(dataWidth/8))
189 throw "Narrow bursts are not supported";
190
191 this->agent->log(0, "Axi write start");
192 }
193 break;
194 case AxiWriteState::W:
195 wready_new = 1;
196 if(*wready == 1 && *wvalid == 1) {
197 writeWord(writeAddr, *wdata, *wstrb);
198 if(*wlast) {
199 writeState = AxiWriteState::B;
200 wready_new = 0;
201 } else {
202 writeAddr += int(dataWidth/8); // TODO: make data width configurable
203 }
204 }
205 break;
206 case AxiWriteState::B:
207 bvalid_new = 1;
208 if(*bready == 1 && *bvalid == 1) {
209 bvalid_new = 0;
210 writeState = AxiWriteState::AW;
211 this->agent->log(0, "Axi write transfer completed");
212 }
213 break;
214 default:
215 writeState = AxiWriteState::AW;
216 break;
217 }
218 }
219
reset()220 void AxiSlave::reset()
221 {
222 *aresetn = 1;
223 tick(true);
224 *aresetn = 0;
225 tick(true);
226 }
227
228 // You can't read/write using slave bus
write(uint64_t addr,uint64_t value)229 void AxiSlave::write(uint64_t addr, uint64_t value)
230 {
231 throw "Unsupported";
232 }
233
read(uint64_t addr)234 uint64_t AxiSlave::read(uint64_t addr)
235 {
236 throw "Unsupported";
237 }
238