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