scc 2026.07
SystemC components library
axi_protocol.cpp
1/*
2 * Copyright 2020-2022 Arteris IP
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <axi/checker/axi_protocol.h>
18#include <scc/report.h>
19namespace axi {
20namespace checker {
21
22void axi_protocol::fw_pre(const axi_protocol::payload_type& trans, const axi_protocol::phase_type& phase) {
23 auto cmd = trans.get_command();
24 if(cmd == tlm::TLM_IGNORE_COMMAND)
25 SCCERR(name) << "Illegal command: tlm::TLM_IGNORE_COMMAND on forward path";
26 if(check_phase_change(trans, phase))
27#ifndef NCSC
28 SCCERR(name) << "Illegal phase transition: " << phase.get_name() << " on forward path";
29#else
30 SCCERR(name) << "Illegal phase transition: " << phase << " on forward path";
31#endif
32}
33
34void axi_protocol::fw_post(const axi_protocol::payload_type& trans, const axi_protocol::phase_type& phase, tlm::tlm_sync_enum rstat) {
35 if(rstat == tlm::TLM_ACCEPTED)
36 return;
37 auto cmd = trans.get_command();
38 if(req_beat[cmd] == tlm::BEGIN_REQ && (phase == tlm::BEGIN_RESP || phase == axi::BEGIN_PARTIAL_RESP))
39 req_beat[cmd] = tlm::UNINITIALIZED_PHASE;
40 if(check_phase_change(trans, phase))
41#ifndef NCSC
42 SCCERR(name) << "Illegal phase transition: " << phase.get_name() << " on return in forward path";
43#else
44 SCCERR(name) << "Illegal phase transition: " << phase << " on return in forward path";
45#endif
46}
47
48void axi_protocol::bw_pre(const axi_protocol::payload_type& trans, const axi_protocol::phase_type& phase) {
49 auto cmd = trans.get_command();
50 if(cmd == tlm::TLM_IGNORE_COMMAND)
51 SCCERR(name) << "Illegal command: tlm::TLM_IGNORE_COMMAND on forward path";
52 if(check_phase_change(trans, phase))
53#ifndef NCSC
54 SCCERR(name) << "Illegal phase transition: " << phase.get_name() << " on backward path";
55#else
56 SCCERR(name) << "Illegal phase transition: " << phase << " on backward path";
57#endif
58}
59
60void axi_protocol::bw_post(const axi_protocol::payload_type& trans, const axi_protocol::phase_type& phase, tlm::tlm_sync_enum rstat) {
61 if(rstat == tlm::TLM_ACCEPTED)
62 return;
63 if(check_phase_change(trans, phase))
64#ifndef NCSC
65 SCCERR(name) << "Illegal phase transition: " << phase.get_name() << " on return in backward path";
66#else
67 SCCERR(name) << "Illegal phase transition: " << phase << " on return in backward path";
68#endif
69}
70
71bool axi_protocol::check_phase_change(payload_type const& trans, const axi_protocol::phase_type& phase) {
72 // phase tests
73 auto cur_req = req_beat[trans.get_command()];
74 auto cur_resp = resp_beat[trans.get_command()];
75 bool error{false};
76 if(phase == tlm::BEGIN_REQ || phase == axi::BEGIN_PARTIAL_REQ) {
77 error |= cur_req != tlm::UNINITIALIZED_PHASE;
78 cur_req = phase;
79 } else if(phase == axi::END_PARTIAL_REQ) {
80 error |= cur_req != axi::BEGIN_PARTIAL_REQ;
81 cur_req = tlm::UNINITIALIZED_PHASE;
82 } else if(phase == tlm::END_REQ) {
83 error |= cur_req != tlm::BEGIN_REQ;
84 cur_req = tlm::UNINITIALIZED_PHASE;
85 } else if(phase == tlm::BEGIN_RESP || phase == axi::BEGIN_PARTIAL_RESP) {
86 error |= cur_resp != tlm::UNINITIALIZED_PHASE;
87 cur_resp = phase;
88 } else if(phase == tlm::END_RESP) {
89 error |= cur_resp != tlm::BEGIN_RESP;
90 cur_resp = tlm::UNINITIALIZED_PHASE;
91 } else if(phase == axi::END_PARTIAL_RESP) {
92 error |= cur_resp != axi::BEGIN_PARTIAL_RESP;
93 cur_resp = tlm::UNINITIALIZED_PHASE;
94 } else {
95 error |= true;
96 }
97 if(req_beat[trans.get_command()] != cur_req) {
98 req_beat[trans.get_command()] = cur_req;
99 request_update(trans);
100 }
101 if(resp_beat[trans.get_command()] != cur_resp) {
102 resp_beat[trans.get_command()] = cur_resp;
103 response_update(trans);
104 }
105 return error;
106}
107
108void axi_protocol::request_update(const payload_type& trans) {
109 auto axi_id = axi::get_axi_id(trans);
110 auto axi_burst_len = axi::get_burst_length(trans);
111 if(trans.is_write()) {
112 if(req_beat[tlm::TLM_WRITE_COMMAND] == tlm::UNINITIALIZED_PHASE) {
113 req_id[tlm::TLM_WRITE_COMMAND] = umax;
114 } else {
115 if(req_id[tlm::TLM_WRITE_COMMAND] == umax) {
116 req_id[tlm::TLM_WRITE_COMMAND] = axi_id;
117 wr_req_beat_count++;
118 } else if(req_id[tlm::TLM_WRITE_COMMAND] != axi_id) {
119 SCCERR(name) << "Illegal ordering: a transaction with AWID:0x" << std::hex << axi_id
120 << " starts while a transaction with AWID:0x" << req_id[tlm::TLM_WRITE_COMMAND] << " is active";
121 }
122 if(req_beat[tlm::TLM_WRITE_COMMAND] == tlm::BEGIN_REQ) {
123 if(wr_req_beat_count != axi_burst_len) {
124 SCCERR(name) << "Illegal AXI settings: number of transferred beats (" << wr_req_beat_count
125 << ") does not comply with AWLEN:0x" << std::hex << axi::get_burst_length(trans) - 1;
126 }
127 wr_req_beat_count = 0;
128 open_tx_by_id[tlm::TLM_WRITE_COMMAND][axi_id].push_back(reinterpret_cast<uintptr_t>(&trans));
129 check_properties(trans);
130 }
131 }
132 } else if(trans.is_read()) {
133 if(req_beat[tlm::TLM_READ_COMMAND] == tlm::UNINITIALIZED_PHASE) {
134 req_id[tlm::TLM_READ_COMMAND] = umax;
135 } else if(req_beat[tlm::TLM_READ_COMMAND] == tlm::BEGIN_REQ) {
136 if(req_id[tlm::TLM_READ_COMMAND] == umax) {
137 req_id[tlm::TLM_READ_COMMAND] = axi_id;
138 open_tx_by_id[tlm::TLM_READ_COMMAND][axi_id].push_back(reinterpret_cast<uintptr_t>(&trans));
139 } else if(req_id[tlm::TLM_READ_COMMAND] != axi_id) {
140 SCCERR(name) << "Illegal phase: a read transaction uses a phase with id " << req_beat[tlm::TLM_READ_COMMAND];
141 }
142 check_properties(trans);
143 } else {
144 SCCERR(name) << "Illegal phase: a read transaction uses a phase with id " << req_beat[tlm::TLM_READ_COMMAND];
145 }
146 }
147}
148
149void axi_protocol::response_update(const payload_type& trans) {
150 auto axi_id = axi::get_axi_id(trans);
151 auto axi_burst_len = axi::get_burst_length(trans);
152 if(trans.is_write()) {
153 if(resp_beat[tlm::TLM_WRITE_COMMAND] == tlm::UNINITIALIZED_PHASE) {
154 resp_id[tlm::TLM_WRITE_COMMAND] = umax;
155 } else if(resp_beat[tlm::TLM_WRITE_COMMAND] == tlm::BEGIN_RESP) {
156 if(resp_id[tlm::TLM_WRITE_COMMAND] == umax) {
157 resp_id[tlm::TLM_WRITE_COMMAND] = axi_id;
158 if(open_tx_by_id[tlm::TLM_WRITE_COMMAND][axi_id].front() != reinterpret_cast<uintptr_t>(&trans)) {
159 SCCERR(name) << "Write response ordering violation: a response with AWID:0x" << std::hex << axi_id
160 << " starts before the previous response with the same id finished";
161 }
162 } else if(resp_id[tlm::TLM_WRITE_COMMAND] != axi_id) {
163 SCCERR(name) << "Illegal phase: a read transaction uses a phase with id " << resp_beat[tlm::TLM_WRITE_COMMAND];
164 }
165 open_tx_by_id[tlm::TLM_WRITE_COMMAND][axi_id].pop_front();
166 } else {
167 SCCERR(name) << "Illegal phase: a read transaction uses a phase with id " << resp_beat[tlm::TLM_WRITE_COMMAND];
168 }
169 } else if(trans.is_read()) {
170 if(resp_beat[tlm::TLM_READ_COMMAND] == tlm::UNINITIALIZED_PHASE) {
171 resp_id[tlm::TLM_READ_COMMAND] = umax;
172 } else {
173 if(resp_id[tlm::TLM_READ_COMMAND] == umax) {
174 resp_id[tlm::TLM_READ_COMMAND] = axi_id;
175 if(open_tx_by_id[tlm::TLM_READ_COMMAND][axi_id].front() != reinterpret_cast<uintptr_t>(&trans)) {
176 SCCERR(name) << "Read response ordering violation: a response with ARID:0x" << std::hex << axi_id
177 << " starts before the previous response with the same id finished";
178 }
179 rd_resp_beat_count[axi_id]++;
180 } else if(resp_id[tlm::TLM_READ_COMMAND] != axi_id) {
181 SCCERR(name) << "Illegal phase: a read transaction uses a phase with id " << resp_beat[tlm::TLM_READ_COMMAND];
182 }
183 if(resp_beat[tlm::TLM_READ_COMMAND] == tlm::BEGIN_RESP) {
184 if(rd_resp_beat_count[axi_id] != axi_burst_len) {
185 SCCERR(name) << "Illegal AXI settings: number of transferred beats (" << wr_req_beat_count
186 << ") does not comply with AWLEN:0x" << std::hex << axi::get_burst_length(trans) - 1;
187 }
188 open_tx_by_id[tlm::TLM_READ_COMMAND][axi_id].pop_front();
189 rd_resp_beat_count[axi_id] = 0;
190 }
191 }
192 }
193}
194
195void axi_protocol::check_datawith_settings(payload_type const& trans) {
196 auto axi_burst_len = axi::get_burst_length(trans);
197 auto axi_burst_size = axi::get_burst_size(trans);
198 auto mask = bw - 1ULL;
199 auto offset = trans.get_address() & mask;
200 if(!offset) {
201 if(trans.get_data_length() != (axi_burst_size * axi_burst_len)) {
202 SCCERR(name) << "Illegal AXI settings: transaction data length (" << trans.get_data_length()
203 << ") does not correspond to AxSIZE/AxLEN setting (" << axi_burst_size << "/" << axi_burst_len - 1
204 << ") and buswidth " << bw << " for " << trans;
205 }
206 } else {
207 if((trans.get_data_length() + offset) > (axi_burst_size * axi_burst_len)) {
208 SCCERR(name) << "Illegal AXI settings: transaction data length (" << trans.get_data_length()
209 << ") does not correspond to AxSIZE/AxLEN setting (" << axi_burst_size << "/" << axi_burst_len - 1
210 << ") and buswidth " << bw << " for " << trans;
211 }
212 }
213}
214constexpr unsigned comb(axi::bar_e bar, axi::domain_e domain, axi::snoop_e snoop) {
215 return to_int(bar) << 10 | to_int(domain) << 8 | to_int(snoop);
216};
217
218void axi_protocol::check_properties(const payload_type& trans) {
219 if(auto* axi4_ext = trans.get_extension<axi::axi4_extension>()) {
220 check_datawith_settings(trans);
221 if(axi4_ext->get_cache() & 0xc0) {
222 if(!axi4_ext->get_cache())
223 SCCERR(name) << "Illegal AXI settings: active allocate bit(s) requires modifiable bit set";
224 }
225 if(axi4_ext->get_qos() > 15)
226 SCCERR(name) << "Illegal AXI4 settings: maximum value of QoS is 15, illegal value is " << axi4_ext->get_qos();
227 } else if(auto* ace_ext = trans.get_extension<axi::ace_extension>()) {
228 if(ace_ext->get_cache() & 0xc0) {
229 if(!ace_ext->get_cache())
230 SCCERR(name) << "Illegal ACEL settings: active allocate bit(s) requires modifiable bit set";
231 auto snoop = ace_ext->get_snoop();
232 auto domain = ace_ext->get_domain();
233 auto bar = ace_ext->get_barrier();
234 switch(comb(bar, domain, snoop)) {
235 case comb(bar_e::RESPECT_BARRIER, domain_e::NON_SHAREABLE, snoop_e::READ_NO_SNOOP):
236 case comb(bar_e::RESPECT_BARRIER, domain_e::SYSTEM, snoop_e::READ_NO_SNOOP):
237 case comb(bar_e::RESPECT_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::READ_ONCE):
238 case comb(bar_e::RESPECT_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::READ_ONCE):
239 case comb(bar_e::MEMORY_BARRIER, domain_e::NON_SHAREABLE, snoop_e::READ_NO_SNOOP):
240 case comb(bar_e::MEMORY_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::READ_ONCE):
241 case comb(bar_e::MEMORY_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::READ_ONCE):
242 case comb(bar_e::MEMORY_BARRIER, domain_e::SYSTEM, snoop_e::READ_NO_SNOOP):
243 check_datawith_settings(trans);
244 break;
245 case comb(bar_e::RESPECT_BARRIER, domain_e::NON_SHAREABLE, snoop_e::CLEAN_SHARED):
246 case comb(bar_e::RESPECT_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::CLEAN_SHARED):
247 case comb(bar_e::RESPECT_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::CLEAN_SHARED):
248 case comb(bar_e::RESPECT_BARRIER, domain_e::NON_SHAREABLE, snoop_e::CLEAN_INVALID):
249 case comb(bar_e::RESPECT_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::CLEAN_INVALID):
250 case comb(bar_e::RESPECT_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::CLEAN_INVALID):
251 case comb(bar_e::RESPECT_BARRIER, domain_e::NON_SHAREABLE, snoop_e::MAKE_INVALID):
252 case comb(bar_e::RESPECT_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::MAKE_INVALID):
253 case comb(bar_e::RESPECT_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::MAKE_INVALID):
254 break;
255 case comb(bar_e::RESPECT_BARRIER, domain_e::NON_SHAREABLE, snoop_e::WRITE_NO_SNOOP):
256 case comb(bar_e::RESPECT_BARRIER, domain_e::SYSTEM, snoop_e::WRITE_NO_SNOOP):
257 case comb(bar_e::RESPECT_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::WRITE_UNIQUE):
258 case comb(bar_e::RESPECT_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::WRITE_UNIQUE):
259 case comb(bar_e::RESPECT_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::WRITE_LINE_UNIQUE):
260 case comb(bar_e::RESPECT_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::WRITE_LINE_UNIQUE):
261 case comb(bar_e::MEMORY_BARRIER, domain_e::NON_SHAREABLE, snoop_e::WRITE_NO_SNOOP):
262 case comb(bar_e::MEMORY_BARRIER, domain_e::INNER_SHAREABLE, snoop_e::WRITE_UNIQUE):
263 case comb(bar_e::MEMORY_BARRIER, domain_e::OUTER_SHAREABLE, snoop_e::WRITE_UNIQUE):
264 case comb(bar_e::MEMORY_BARRIER, domain_e::SYSTEM, snoop_e::WRITE_NO_SNOOP):
265 check_datawith_settings(trans);
266 break;
267 default:
268 SCCERR(name) << "Illegal ACEL settings: According to D11.2 ACE-Lite signal requirements of ARM IHI 0022H the following "
269 "setting is illegal:\n"
270 << "AxBAR:" << to_char(bar) << ", AxDOMAIN:" << to_char(domain) << ", AxSNOOP:" << to_char(snoop);
271 }
272 }
273 } else if(auto* axi3_ext = trans.get_extension<axi::axi4_extension>()) {
274 if(axi3_ext->get_cache() & 0xc0) {
275 if(!axi3_ext->get_cache())
276 SCCERR(name) << "Illegal AXI settings: active allocate bit(s) requires modifiable bit set";
277 }
278 }
279}
280
281} /* namespace checker */
282} /* namespace axi */
TLM2.0 components modeling AHB.
const char * to_char(E t)
unsigned get_burst_length(const request &r)
Definition axi_tlm.h:1157
domain_e
Definition axi_tlm.h:84
bar_e
Definition axi_tlm.h:88
@ MEMORY_BARRIER
Normal access, respecting barriers.
Definition axi_tlm.h:90
snoop_e
Definition axi_tlm.h:98
constexpr ULT to_int(E t)
Definition axi_tlm.h:46
unsigned get_burst_size(const request &r)
Definition axi_tlm.h:1190