Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: ucb_monitor.vr | |
4 | // Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved | |
5 | // 4150 Network Circle, Santa Clara, California 95054, U.S.A. | |
6 | // | |
7 | // * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
8 | // | |
9 | // This program is free software; you can redistribute it and/or modify | |
10 | // it under the terms of the GNU General Public License as published by | |
11 | // the Free Software Foundation; version 2 of the License. | |
12 | // | |
13 | // This program is distributed in the hope that it will be useful, | |
14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | // GNU General Public License for more details. | |
17 | // | |
18 | // You should have received a copy of the GNU General Public License | |
19 | // along with this program; if not, write to the Free Software | |
20 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | // | |
22 | // For the avoidance of doubt, and except that if any non-GPL license | |
23 | // choice is available it will apply instead, Sun elects to use only | |
24 | // the General Public License version 2 (GPLv2) at this time for any | |
25 | // software where a choice of GPL license versions is made | |
26 | // available with the language indicating that GPLv2 or any later version | |
27 | // may be used, or where a choice of which version of the GPL is applied is | |
28 | // otherwise unspecified. | |
29 | // | |
30 | // Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
31 | // CA 95054 USA or visit www.sun.com if you need additional information or | |
32 | // have any questions. | |
33 | // | |
34 | // ========== Copyright Header End ============================================ | |
35 | #include <vera_defines.vrh> | |
36 | #include <ListMacros.vrh> // Vera's linked list | |
37 | #include "std_display_class.vrh" | |
38 | #include "ucb_defines.vri" | |
39 | #include "ucb___packet.vrh" | |
40 | #include "ucb_top.vri" | |
41 | ||
42 | // creating a list. NOTE: it's a macro, so no ';' | |
43 | MakeVeraList(UCB___packet) | |
44 | ||
45 | class UCB_monitor { | |
46 | //---public vars--- | |
47 | event req_begin; // triggered when NCU starts a new request | |
48 | event rsp_begin; // triggered when target blk starts a response to NCU | |
49 | event req_end; // triggered when NCU completes a request. Also, req_pkt is updated | |
50 | event rsp_end; // triggered when target blk completes a response. Also, rsp_pkt is updated. | |
51 | integer ignore_err = 0; // ignore all errors | |
52 | //---ports and classes--- | |
53 | local UCB_port ucb_port; // UCB port | |
54 | local StandardDisplay dbg; // Standard display for printing | |
55 | //---local vars--- | |
56 | local string dispScope; // for standard display | |
57 | local integer bus_width; // width of data bus. Valid values: 4 or 8 bits | |
58 | local bit [127:0] req_pkt; // the latest request pkt which is updated when a new req is completed | |
59 | local bit [127:0] rsp_pkt; // the latest response pkt which is updated when a new rsp is completed | |
60 | VeraList_UCB___packet pend_rd_reqs; // list of pending read requests | |
61 | local bit [7:0] g_asi; // global addr identifier (ie. [39:32] of PA or UCB pkt bits [54:47]) | |
62 | local integer ntransfer; // number of data transfers for one req/rsp (ie. 128/bus_width) | |
63 | local integer num_req; // number of requests | |
64 | local integer num_rsp; // number of responses | |
65 | local integer running; // init to 0. Set to 1 when this monitor starts | |
66 | local integer error_cnt; // Error count. Init to 0. WARN: become negative if exeed max integer | |
67 | local integer max_error_printed; // stop print out error message when (error_cnt > max_error_printed) | |
68 | local integer semaphore_id; // semaphore for accessing Vera list pend_rd_reqs | |
69 | ||
70 | //---public subroutines--- | |
71 | task new(string dispScope="UCB_monitor", StandardDisplay dbg, UCB_port ucb_port, integer bus_width, bit [7:0] g_asi, integer start_it=0); | |
72 | task start(); // start the monitor. Do nothing if the monitor is already running | |
73 | function UCB___packet get_req_pkt(); | |
74 | function UCB___packet get_rsp_pkt(); | |
75 | function integer get_error_cnt() { get_error_cnt = this.error_cnt; } | |
76 | task ignore_errors() { this.ignore_err = 1; } | |
77 | ||
78 | //--- local subroutines --- | |
79 | local task monitor_req(); // monitor the request bus | |
80 | local task monitor_rsp(); // monitor the response bus | |
81 | local function bit [127:0] get_request_pkt(); // return this.req_pkt | |
82 | local function bit [127:0] get_response_pkt(); // return this.rsp_pkt | |
83 | task print_error_msg(integer err_cnt, string error_msg); | |
84 | } | |
85 | ||
86 | //################################################################ | |
87 | //######### implementation of subroutines ########### | |
88 | //################################################################ | |
89 | ||
90 | task UCB_monitor::new(string dispScope="UCB_monitor", StandardDisplay dbg, UCB_port ucb_port, integer bus_width, bit [7:0] g_asi, integer start_it=0) { | |
91 | //---from arg list--- | |
92 | this.dispScope = dispScope; | |
93 | this.dbg = dbg; | |
94 | this.ucb_port = ucb_port; | |
95 | this.bus_width = bus_width; | |
96 | this.g_asi = g_asi; | |
97 | //---the rest--- | |
98 | this.req_pkt = 128'h0; // init to bad value | |
99 | this.rsp_pkt = 128'h0; // init to bad value | |
100 | this.pend_rd_reqs = new(); // empty list | |
101 | this.ntransfer = 128 / bus_width; | |
102 | this.num_req = 0; | |
103 | this.num_rsp = 0; | |
104 | this.running = 0; // monitor is not running yet | |
105 | this.error_cnt = 0; | |
106 | this.max_error_printed = 10; | |
107 | semaphore_id = alloc(SEMAPHORE, 0, 1, 1); // 0: semaphore ID, 1: semaphore count, 1: one key initially | |
108 | if (semaphore_id == 0) | |
109 | dbg.dispmon(this.dispScope, MON_ERR, "alloc(SEMAPHORE, 0, 1, 1) : failed"); | |
110 | ||
111 | //---sanity check--- | |
112 | if ((bus_width != 4) && (bus_width != 8)) { | |
113 | dbg.dispmon(this.dispScope, MON_ERR, psprintf("ucb bus width %0d <= monitor only supports 4/8-bit bus", this.bus_width)); | |
114 | this.error_cnt++; | |
115 | } | |
116 | //---start back ground threads--- | |
117 | if (start_it) | |
118 | this.start(); // start the monitor | |
119 | } | |
120 | ||
121 | //========================================================== | |
122 | // WHAT: start the monitor | |
123 | //========================================================== | |
124 | task UCB_monitor::start() { | |
125 | if (running) { | |
126 | return; // monitor is already running | |
127 | } | |
128 | dbg.dispmon(this.dispScope, MON_INFO, "UCB monitor starts"); | |
129 | fork { monitor_req(); } join none | |
130 | fork { monitor_rsp(); } join none | |
131 | running = 1; // monitor is now running | |
132 | } | |
133 | ||
134 | //========================================================== | |
135 | // WHAT: monitor the requests from NCU to target unit. | |
136 | // WARNING: per NCU and UCB specs, NCU must deassert ncu_xxx_vld | |
137 | // for at least one cycle before sending the next request. | |
138 | //========================================================== | |
139 | task UCB_monitor::monitor_req() { | |
140 | while (1) { | |
141 | @(posedge ucb_port.$req_vld); // wait for new request | |
142 | this.num_req++; | |
143 | trigger(this.req_begin); // inform the caller | |
144 | this.req_pkt = get_request_pkt(); // get the pkt | |
145 | trigger(this.req_end); // inform the caller | |
146 | } | |
147 | } | |
148 | ||
149 | //========================================================== | |
150 | // WHAT: monitor the response from target unit to NCU. | |
151 | // WARNING: per NCU and UCB specs, target unit must deassert | |
152 | // xxx_ncu_vld for at least one cycle before sending the next response. | |
153 | //========================================================== | |
154 | task UCB_monitor::monitor_rsp() { | |
155 | while (1) { | |
156 | @(posedge ucb_port.$rsp_vld); // wait for new response | |
157 | this.num_rsp++; | |
158 | trigger(this.rsp_begin); // inform the caller | |
159 | this.rsp_pkt = get_response_pkt(); // get the pkt | |
160 | trigger(this.rsp_end); // inform the caller | |
161 | } | |
162 | } | |
163 | ||
164 | //========================================================== | |
165 | // WHAT: get the request packet (ie. 128-bit pkt NCU sends to target block | |
166 | // WARNING: per NCU and UCB specs, NCU must deassert ncu_xxx_vld | |
167 | // for at least one cycle before sending the next request. | |
168 | //========================================================== | |
169 | function bit [127:0] UCB_monitor::get_request_pkt() { | |
170 | integer i, lsb=0, req_err_cnt = 0; | |
171 | bit [127:0] data; | |
172 | string pkt_type_str; | |
173 | UCB___packet pkt; | |
174 | ||
175 | //---get req pkt from the bus--- | |
176 | fork { | |
177 | @(negedge ucb_port.$req_vld); | |
178 | dbg.dispmon(this.dispScope, MON_ERR, "NCU deasserts ncu_xxx_vld before ucb req pkt completed"); | |
179 | } | |
180 | { | |
181 | for (i = 0; i < this.ntransfer; i++) { | |
182 | if (ucb_port.$req_stall == 1'b1) | |
183 | @(negedge ucb_port.$req_stall); | |
184 | data[lsb + bus_width - 1 : lsb] = ucb_port.$req_data; | |
185 | if (i != (this.ntransfer - 1)) // not the last data transfer | |
186 | @(posedge ucb_port.$clk); | |
187 | lsb = lsb + bus_width; // for next data transfer | |
188 | } | |
189 | } join any | |
190 | terminate; | |
191 | //--- NCU must deassert ncu_xxx_vld for at least one cycle---- | |
192 | fork { | |
193 | @(posedge ucb_port.$clk); | |
194 | if (ucb_port.$req_vld != 1'b0) | |
195 | dbg.dispmon(this.dispScope, MON_ERR, "NCU does not deassert ncu_xxx_vld after request completed"); | |
196 | } join none | |
197 | //---print out req for info--- | |
198 | pkt = new(*, data); | |
199 | case (pkt.pkt_type) { | |
200 | UCB_PKT_READ_REQ: pkt_type_str = "rd req"; | |
201 | UCB_PKT_WRITE_REQ: pkt_type_str = "wr req"; | |
202 | default: pkt_type_str = "unkown req"; | |
203 | } | |
204 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s : addr=0x%h, payload=0x%h, threadID=0x%h, cpuID=0x%h, bufferID=0x%h", | |
205 | pkt_type_str, pkt.addr, pkt.payload, pkt.thread_id, pkt.cpu_id, pkt.buffer_id)); | |
206 | //---check for protocol/restriction violations--- | |
207 | if (pkt.req_size !== UCB_PKT_REQSIZE__8B) | |
208 | print_error_msg(req_err_cnt++, psprintf("ucb req pkt=0x%h <= not 8B req", data)); | |
209 | if ((pkt.pkt_type != UCB_PKT_READ_REQ) && (pkt.pkt_type != UCB_PKT_WRITE_REQ)) | |
210 | print_error_msg(req_err_cnt++, psprintf("ucb req pkt=0x%h <= not rd/wr req", data)); | |
211 | if (pkt.addr[39:32] != this.g_asi) | |
212 | print_error_msg(req_err_cnt++, psprintf("ucb req pkt=0x%h <= global asi or [54:47] is not 0x%h ", data, this.g_asi)); | |
213 | if ((pkt.buffer_id != 2'b00) && (pkt.buffer_id != 2'b01)) | |
214 | print_error_msg(req_err_cnt++, psprintf("ucb req pkt=0x%h <= buffer ID or [11:10] is bad", data)); | |
215 | //---for read request--- | |
216 | if (pkt.pkt_type == UCB_PKT_READ_REQ) { | |
217 | void = semaphore_get(WAIT, semaphore_id, 1); // only one thread can access pending rd list. | |
218 | this.pend_rd_reqs.push_back(pkt); // add new rd req to the end of the list | |
219 | semaphore_put(semaphore_id, 1); | |
220 | } | |
221 | //---return value--- | |
222 | get_request_pkt = data; | |
223 | } | |
224 | ||
225 | //========================================================== | |
226 | // WHAT: get the response packet (ie. 128-bit pkt target block sent to NCU) | |
227 | // WARNING: per NCU and UCB specs, target unit must deassert | |
228 | // xxx_ncu_vld for at least one cycle before sending the next response. | |
229 | //========================================================== | |
230 | function bit [127:0] UCB_monitor::get_response_pkt() { | |
231 | integer i, lsb=0, rsp_err_cnt=0; | |
232 | bit [127:0] data; | |
233 | bit [3:0] pkt_type; | |
234 | string pkt_type_str; | |
235 | UCB___packet pkt, oldest_rd_pkt; | |
236 | integer is_rd_nack=0; // 1: read_nack rsp, 0: not a read_nack rsp | |
237 | integer nbits=0; // number of bits transferred | |
238 | ||
239 | //--- sanity check --- | |
240 | if (pend_rd_reqs.size() < 1) | |
241 | print_error_msg(0, "got rd rsp, but there is no pending rd req"); | |
242 | //---get response pkt--- | |
243 | fork { | |
244 | @(negedge ucb_port.$rsp_vld); | |
245 | dbg.dispmon(this.dispScope, MON_ERR, "target unit deasserts *_ncu_vld before completing rd response pkt"); | |
246 | } | |
247 | { | |
248 | for (i = 0; i < this.ntransfer; i++) { | |
249 | if (ucb_port.$rsp_stall == 1'b1) | |
250 | @(negedge ucb_port.$rsp_stall); | |
251 | data[lsb + bus_width - 1 : lsb] = ucb_port.$rsp_data; | |
252 | nbits += this.bus_width; | |
253 | //---check for READ_NACK--- | |
254 | if (i == 0) { | |
255 | pkt_type = data[UCB_PKT_PKTTYPE__MSB:UCB_PKT_PKTTYPE__LSB]; | |
256 | case (pkt_type) { | |
257 | UCB_PKT_READ_ACK : is_rd_nack = 0; | |
258 | UCB_PKT_READ_NACK : is_rd_nack = 1; | |
259 | default : | |
260 | print_error_msg(rsp_err_cnt++, psprintf("ucb rsp pkt type=0x%h <= not READ_ACK nor READ_NACK rsp", pkt_type)); | |
261 | } | |
262 | } | |
263 | //---for next data transfer--- | |
264 | if ((is_rd_nack) && (nbits >= 64)) | |
265 | break; // in read_nack, client sends UCB pkt without payload (ie. 64-bit pkt only) | |
266 | if (i != (this.ntransfer - 1)) // not the last response | |
267 | @(posedge ucb_port.$clk); | |
268 | lsb = lsb + bus_width; | |
269 | } | |
270 | } join any | |
271 | terminate; | |
272 | //---- target unit must deassert xxx_ncu_vld for at least one cycle--- | |
273 | fork { | |
274 | @(negedge ucb_port.$clk); | |
275 | if (ucb_port.$rsp_vld != 1'b0) | |
276 | dbg.dispmon(this.dispScope, MON_ERR, "target unit does not deassert *_ncu_vld after rd response completed"); | |
277 | } join none | |
278 | //----print out rsp pkt for info---- | |
279 | pkt = new(*, data); | |
280 | case (pkt.pkt_type) { | |
281 | UCB_PKT_READ_ACK : pkt_type_str = "rd rsp"; | |
282 | UCB_PKT_READ_NACK : pkt_type_str = "rd nack"; | |
283 | default: pkt_type_str = "unkown rsp"; | |
284 | } | |
285 | dbg.dispmon(this.dispScope, MON_INFO, psprintf("%s : payload=0x%h, threadID=0x%h, cpuID=0x%h, bufferID=0x%h", | |
286 | pkt_type_str, pkt.payload, pkt.thread_id, pkt.cpu_id, pkt.buffer_id)); | |
287 | //---check rsp pkt header matches rd req pkt--- | |
288 | if (pend_rd_reqs.size() > 0) { // check only when there is a pending rd req | |
289 | void = semaphore_get(WAIT, semaphore_id, 1); // only one thread can access pending rd list. | |
290 | oldest_rd_pkt = pend_rd_reqs.front(); | |
291 | if (pkt.buffer_id != oldest_rd_pkt.buffer_id) | |
292 | print_error_msg(0, psprintf("ucb rsp pkt=0x%h, req pkt=0x%h <= buffer ID or [11:10] not matched", data, oldest_rd_pkt.pkt)); | |
293 | if (pkt.cpu_id != oldest_rd_pkt.cpu_id) | |
294 | print_error_msg(0, psprintf("ucb rsp pkt=0x%h, req pkt=0x%h <= CPU ID or [9:7] not matched", data, oldest_rd_pkt.pkt)); | |
295 | if (pkt.thread_id != oldest_rd_pkt.thread_id) | |
296 | print_error_msg(0, psprintf("ucb rsp pkt=0x%h, req pkt=0x%h <= thread ID or [6:4] not matched", data, oldest_rd_pkt.pkt)); | |
297 | pend_rd_reqs.pop_front(); // remove oldest pending read req from list, no matter rsp matches or not | |
298 | semaphore_put(semaphore_id, 1); | |
299 | } | |
300 | //---rd req is now completed--- | |
301 | get_response_pkt = data; | |
302 | } | |
303 | ||
304 | //============================================================= | |
305 | // WHAT: print error message | |
306 | // NOTE: | |
307 | // err_cnt is local error count of a particular error type. | |
308 | // this.error_cnt is global error count of this vera class. | |
309 | //============================================================= | |
310 | task UCB_monitor::print_error_msg(integer err_cnt, string error_msg) { | |
311 | this.error_cnt++; // increment global error count | |
312 | if (this.ignore_err) | |
313 | return; | |
314 | if (err_cnt <= max_error_printed) | |
315 | dbg.dispmon(this.dispScope, MON_ERR, error_msg); | |
316 | } | |
317 | ||
318 | //============================================================= | |
319 | // Return the req pkt to the caller | |
320 | //============================================================= | |
321 | function UCB___packet UCB_monitor::get_req_pkt() { | |
322 | UCB___packet ucb_pkt = new(*, this.req_pkt); // create a separate copy | |
323 | get_req_pkt = ucb_pkt; | |
324 | } | |
325 | ||
326 | //============================================================= | |
327 | // Return rsp pkt to the caller | |
328 | //============================================================= | |
329 | function UCB___packet UCB_monitor::get_rsp_pkt() { | |
330 | UCB___packet ucb_pkt = new(*, this.rsp_pkt); // create a separate copy | |
331 | get_rsp_pkt = ucb_pkt; | |
332 | } | |
333 | ||
334 |