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