| 1 | // ========== Copyright Header Begin ========================================== |
| 2 | // |
| 3 | // OpenSPARC T2 Processor File: pio_driver.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 "pio.port.vri" |
| 37 | #include "cMesg.vrh" |
| 38 | // #include "ncu_stub.vrh" |
| 39 | #include "niu_gen_pio.vrh" |
| 40 | |
| 41 | extern Mesg be_msg; |
| 42 | // extern Cncu_stub ncu_driver; |
| 43 | extern niu_gen_pio gen_pio_drv; |
| 44 | |
| 45 | class pio_drv { |
| 46 | |
| 47 | bit [39:0] address; // msb = 0 real access |
| 48 | // msb = 1 force register |
| 49 | bit [31:0] wr_data; // data for pio_wr/pio_cfg_wr |
| 50 | bit [31:0] rd_data; // data returned for pio_rd/pio_cfg_rd |
| 51 | bit rd_wr; // 1 = read; 0 = write |
| 52 | bit cfg_access; // 1 = cfg_access; 0 = register access |
| 53 | bit ht_pio = 1'b0; // 1 = ht_bus_access to register; 0 = pio_driver access |
| 54 | integer pio_access; // flag to prevent multiple use of database |
| 55 | event pio_start; // signal to start the primary task |
| 56 | event pio_complete; // return signal to initiator |
| 57 | event pio_checker_start; // signal to have the pio checker |
| 58 | // anticipate pending activity |
| 59 | bit expect_pio_err = 1'b0; // = 1'b1, when where is a pio_error expected. |
| 60 | |
| 61 | task new((pio_port port_bind = null)) ; |
| 62 | |
| 63 | task init_outputs(pio_port port_bind) ; |
| 64 | |
| 65 | task float_outputs(pio_port port_bind) ; |
| 66 | |
| 67 | task ht_reset( pio_port port_bind, |
| 68 | (integer no_of_clocks = 110) |
| 69 | ) ; |
| 70 | |
| 71 | task pio_wr( bit [39:0] addr, bit [31:0] write_data, |
| 72 | (bit exp_pio_err = 1'b0) ) ; |
| 73 | |
| 74 | task pio_rd( bit [39:0] addr, var bit [31:0] read_data, |
| 75 | (bit [31:0] exp_data = 32'b0), |
| 76 | (bit [31:0] data_mask = 32'hFFFF_FFFF), |
| 77 | bit exp_data_valid, |
| 78 | (bit exp_pio_err = 1'b0) ); |
| 79 | |
| 80 | task pio(pio_port port_bind) ; |
| 81 | |
| 82 | task pio_handshake (pio_port port_bind) ; |
| 83 | |
| 84 | task ht_pio_enable () ; |
| 85 | |
| 86 | } |
| 87 | |
| 88 | /* ********************************************************** |
| 89 | init_outputs |
| 90 | Initialize the output signals |
| 91 | ********************************************************** */ |
| 92 | task pio_drv::new((pio_port port_bind = null)) { |
| 93 | |
| 94 | |
| 95 | pio_port my_port = port_bind ; |
| 96 | // create a sempahore for this class |
| 97 | pio_access = alloc(SEMAPHORE, 0, 1, 1); |
| 98 | if(!ht_pio) { |
| 99 | // execute primary tasks |
| 100 | fork |
| 101 | // init_outputs(my_port); |
| 102 | pio(my_port); |
| 103 | join none |
| 104 | |
| 105 | } |
| 106 | // ht_reset(my_port); |
| 107 | } |
| 108 | |
| 109 | |
| 110 | /* ********************************************************** |
| 111 | init_outputs |
| 112 | Initialize the output signals |
| 113 | ********************************************************** */ |
| 114 | |
| 115 | task pio_drv::init_outputs(pio_port port_bind) { |
| 116 | |
| 117 | port_bind.$reg_offset = 0; |
| 118 | port_bind.$write_data = 32'h0000_0000; |
| 119 | port_bind.$rd_wr = 0; |
| 120 | port_bind.$mod_select = 0; |
| 121 | port_bind.$reset = 1'b0; |
| 122 | |
| 123 | } |
| 124 | |
| 125 | task pio_drv::float_outputs(pio_port port_bind) { |
| 126 | |
| 127 | port_bind.$reg_offset = 24'hz; |
| 128 | port_bind.$write_data = 32'hz; |
| 129 | port_bind.$rd_wr = 1'bz; |
| 130 | port_bind.$mod_select = 1'bz; |
| 131 | |
| 132 | repeat(5) @(posedge CLOCK); |
| 133 | |
| 134 | } |
| 135 | |
| 136 | /* ********************************************************** |
| 137 | ht_reset |
| 138 | All the blocks within Vega (FFL, ILC, IRX, ITX, MAC, NRX, NTX, |
| 139 | PHY, PSEUDO_DDR_IO) are reset by "reset" signal from HT. |
| 140 | The "ht_reset" task mimics that functionality. |
| 141 | ********************************************************** */ |
| 142 | |
| 143 | task pio_drv::ht_reset( pio_port port_bind, |
| 144 | (integer no_of_clocks = 110) |
| 145 | ) |
| 146 | { |
| 147 | printf(" Entering ht_reset \n"); |
| 148 | if (!ht_pio) { |
| 149 | // MAC requirement that the reset is applied two times and |
| 150 | // for a minimum duration of 440ns (110 clock cycles) each time. |
| 151 | |
| 152 | if (no_of_clocks < 110) no_of_clocks = 110; |
| 153 | port_bind.$reset = 1'b0; |
| 154 | repeat (10) @(posedge port_bind.$clock); |
| 155 | |
| 156 | repeat (no_of_clocks) { |
| 157 | port_bind.$reset = 1'b1; |
| 158 | @(posedge port_bind.$clock); |
| 159 | } |
| 160 | |
| 161 | repeat (no_of_clocks) { |
| 162 | port_bind.$reset = 1'b0; |
| 163 | @(posedge port_bind.$clock); |
| 164 | } |
| 165 | |
| 166 | repeat (no_of_clocks) { |
| 167 | port_bind.$reset = 1'b1; |
| 168 | @(posedge port_bind.$clock); |
| 169 | } |
| 170 | |
| 171 | port_bind.$reset = 1'b0; |
| 172 | repeat (5) @(posedge port_bind.$clock); |
| 173 | } // end of if (!ht_pio) |
| 174 | |
| 175 | } |
| 176 | |
| 177 | |
| 178 | /* *********************************************************** |
| 179 | pio_wr |
| 180 | This task performs a PIO write of a VEGA register. In asic |
| 181 | simulation the write is performed across the HT interface; |
| 182 | In module simulation, the driver module will simulate the |
| 183 | GPIO interface. |
| 184 | |
| 185 | The "addr" field is a 33 bit field. If the msb is set to |
| 186 | zero, then the lower 32 bits are used to access the memory |
| 187 | mapped VEGA register. If the msb is set to one, then a |
| 188 | case statement will determine the functionality -- in most |
| 189 | situations the "phony" addresses will (1) access items not |
| 190 | in the memory map, (2) directly force registers to save |
| 191 | simulation time, or (3) access multiple registers in one |
| 192 | command. |
| 193 | |
| 194 | The "wr_data" field is a 32 bit field which contains the |
| 195 | data to be written. |
| 196 | |
| 197 | All writes using this task updates the shadow space. |
| 198 | ********************************************************** */ |
| 199 | |
| 200 | task pio_drv::pio_wr(bit [39:0] addr, bit [31:0] write_data, |
| 201 | (bit exp_pio_err = 1'b0) ) { |
| 202 | |
| 203 | semaphore_get(WAIT, pio_access, 1); |
| 204 | |
| 205 | address = addr; |
| 206 | wr_data = write_data; |
| 207 | rd_wr = 1'b0; |
| 208 | cfg_access = 1'b0; |
| 209 | expect_pio_err = exp_pio_err; |
| 210 | |
| 211 | if (ht_pio) { |
| 212 | // Do nothing! |
| 213 | } |
| 214 | else { |
| 215 | // if msb is one, then access does not go to core |
| 216 | //if (addr[32] != 1) { |
| 217 | trigger(ONE_SHOT, pio_start); |
| 218 | sync(ALL, pio_complete); |
| 219 | //} |
| 220 | } |
| 221 | |
| 222 | semaphore_put(pio_access, 1); |
| 223 | |
| 224 | } |
| 225 | |
| 226 | /* ********************************************************** |
| 227 | pio_rd |
| 228 | This task performs a PIO read of a VEGA register. In asic |
| 229 | simulation the read is performed across the HT interface; |
| 230 | in module simulation, the pio driver will simulate the GIO++ |
| 231 | interface. |
| 232 | |
| 233 | The "addr" field is a 33 bit field. If the msb is set to |
| 234 | zero, then the lower 32 bits are used to access the memory |
| 235 | mapped VEGA register. If the msb is set to one, then a |
| 236 | case statement will determine the functionality -- in most |
| 237 | situations the "phony" addresses will (1) access items not |
| 238 | in the memory map, (2) directly read registers to save |
| 239 | simulation time, or (3) access multiple registers in one |
| 240 | command. |
| 241 | |
| 242 | The "rd_data" field is a 32 bit field which will be loaded |
| 243 | with the data read from the register. |
| 244 | |
| 245 | If a corresponding address exists in the shadow space, then |
| 246 | a comparison will be made with the shadow copy. |
| 247 | ********************************************************** */ |
| 248 | |
| 249 | task pio_drv::pio_rd(bit [39:0] addr, var bit [31:0] read_data, |
| 250 | (bit [31:0] exp_data = 32'b0), |
| 251 | (bit [31:0] data_mask = 32'hFFFF_FFFF), |
| 252 | bit exp_data_valid, |
| 253 | (bit exp_pio_err = 1'b0) ) { |
| 254 | |
| 255 | bit [31:0] reg_data; |
| 256 | semaphore_get(WAIT, pio_access, 1); |
| 257 | address = addr; |
| 258 | rd_wr = 1'b1; |
| 259 | cfg_access = 1'b0; |
| 260 | expect_pio_err = exp_pio_err; |
| 261 | |
| 262 | if (ht_pio) { |
| 263 | // Do nothing!! |
| 264 | } |
| 265 | else { |
| 266 | // if msb is one, then access does not go to core |
| 267 | // if (addr[32] != 1) { |
| 268 | trigger(ONE_SHOT, pio_start); |
| 269 | sync(ALL, pio_complete); |
| 270 | // } |
| 271 | read_data = rd_data; |
| 272 | if ( exp_data_valid) { |
| 273 | if ((rd_data & data_mask) !== (exp_data & data_mask)) { |
| 274 | printf("PIO: pio_rd failure: Data miscompare\n"); |
| 275 | be_msg.print(e_mesg_error, "pio_rd", "pio_driver", |
| 276 | "Data miscompare: Addr: 0x%h, expected: %h, observed: %h, mask: %h \n ", |
| 277 | address, exp_data, rd_data, data_mask); |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | } |
| 282 | |
| 283 | semaphore_put(pio_access, 1); |
| 284 | |
| 285 | } |
| 286 | |
| 287 | /* ********************************************************** |
| 288 | pio |
| 289 | Primary task for the pio driver. |
| 290 | Flow: (1) Enter continuous while loop |
| 291 | (2) Make sure that system is out of reset |
| 292 | This is ensured through pio_tasks.vr |
| 293 | (3) Wait for pio command |
| 294 | (4) Send pio_token to checker |
| 295 | (5) Decode address and perform pio with appropriate |
| 296 | port |
| 297 | (6) signal the originating task that the operation |
| 298 | is complete |
| 299 | (7) add a one clock delay |
| 300 | ********************************************************** */ |
| 301 | |
| 302 | task pio_drv::pio(pio_port port_bind) { |
| 303 | |
| 304 | pio_port my_port = port_bind; |
| 305 | |
| 306 | // eventually replace (1) with a system variable that can turn task on/off |
| 307 | while (1) { |
| 308 | |
| 309 | // Wait for the testbench to issue a pio command |
| 310 | sync(ALL, pio_start); |
| 311 | |
| 312 | // Once the token is received, it should be passed on to the checker. |
| 313 | |
| 314 | trigger(ONE_SHOT, pio_checker_start); |
| 315 | |
| 316 | cfg_access = 0; |
| 317 | |
| 318 | if (cfg_access == 0) { |
| 319 | |
| 320 | pio_handshake (my_port) ; |
| 321 | |
| 322 | } // end of if (cfg_access == 0) |
| 323 | |
| 324 | // Tell the originating task that the pio operation has been completed. |
| 325 | |
| 326 | trigger(ONE_SHOT, pio_complete); |
| 327 | |
| 328 | } // end of while (1) |
| 329 | |
| 330 | } // end of pio task |
| 331 | |
| 332 | |
| 333 | /* ********************************************************** |
| 334 | pio_handshake |
| 335 | |
| 336 | Perform the 250 Mhz handshake across the pio interface. |
| 337 | This task has to drive the proper signals at the proper |
| 338 | time. The checker will verify incoming signals such |
| 339 | as rd_data. |
| 340 | |
| 341 | NOTE: The delay between select asserted and select deasserted |
| 342 | should be programmable between 3 and 4. |
| 343 | ********************************************************** */ |
| 344 | /************************** |
| 345 | task pio_drv::pio_handshake (pio_port port_bind) { |
| 346 | integer MAX_DELAY = 1000; |
| 347 | bit ht_pio_error ; |
| 348 | |
| 349 | @1 port_bind.$reg_offset = address; |
| 350 | port_bind.$write_data = wr_data; |
| 351 | port_bind.$rd_wr = rd_wr; |
| 352 | port_bind.$mod_select = 1; // select must be active for at least |
| 353 | |
| 354 | // $pio_err should be driven by all modules and is active high signal. |
| 355 | // $pio_err should be asserted high only when there is a PIO error condition. |
| 356 | @1,MAX_DELAY port_bind.$pio_ack == 1'b1; |
| 357 | ht_pio_error = port_bind.$pio_err; |
| 358 | |
| 359 | rd_data = port_bind.$read_data; |
| 360 | @(posedge port_bind.$clock); |
| 361 | port_bind.$pio_ack == 1'b0; |
| 362 | if (expect_pio_err != 1'b1) { |
| 363 | port_bind.$pio_err == 1'b0; |
| 364 | } |
| 365 | port_bind.$mod_select = 0; // three clock cycles. |
| 366 | repeat (3) { // make sure process is complete |
| 367 | @(port_bind.$clock); |
| 368 | } |
| 369 | |
| 370 | // Error if the $pio_err signal is asserted with expect_pio_err bit is not set! |
| 371 | if ( (expect_pio_err != 1'b1) & (ht_pio_error == 1'b1) ) { |
| 372 | be_msg.print(e_mesg_error, "pio_handshake", "pio_driver", |
| 373 | "PIO ERROR Asserted for Address = %0h \n ", address); |
| 374 | } |
| 375 | |
| 376 | // Error if the $pio_err signal is NOT asserted with expect_pio_err bit is set! |
| 377 | if ( (expect_pio_err == 1'b1) & (ht_pio_error != 1'b1) ) { |
| 378 | be_msg.print(e_mesg_error, "pio_handshake", "pio_driver", |
| 379 | "PIO ERROR Expected for Address = %0h \n ", address); |
| 380 | } |
| 381 | } |
| 382 | *******************************************/ |
| 383 | |
| 384 | task pio_drv::pio_handshake (pio_port port_bind) { |
| 385 | integer MAX_DELAY = 1000; |
| 386 | bit ht_pio_error ; |
| 387 | integer status; |
| 388 | bit [63:0] local_read_data; |
| 389 | bit [63:0] local_write_data; |
| 390 | local_read_data = {32'h0000_0000,rd_data}; |
| 391 | local_write_data = {32'h0000_0000,wr_data}; |
| 392 | |
| 393 | if(rd_wr == 1) // Read |
| 394 | { |
| 395 | // ncu_driver.read_data(address,local_read_data,status); |
| 396 | gen_pio_drv.pio_rd(address,local_read_data); |
| 397 | /* if((expect_pio_err == 0) && (status != 1)) |
| 398 | printf("ERROR: pio_rd error seen when not expected\n"); |
| 399 | if((expect_pio_err == 1) && (status == 1)) |
| 400 | printf("ERROR: pio_rd error not seen when expected\n"); */ |
| 401 | rd_data = local_read_data[31:0]; |
| 402 | } |
| 403 | else |
| 404 | { |
| 405 | // ncu_driver.write_data(address,local_write_data); |
| 406 | gen_pio_drv.pio_wr(address,local_write_data); |
| 407 | wr_data = local_write_data[31:0]; |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | /* ********************************************************** |
| 412 | ht_pio_enable |
| 413 | Enable the ht_pio bit to indicate that the register |
| 414 | acceses will be done from HT interface and not the PIO |
| 415 | slave interface driver. |
| 416 | ********************************************************** */ |
| 417 | task pio_drv::ht_pio_enable() { |
| 418 | |
| 419 | // set the ht_pio bit to enable HT interface access |
| 420 | ht_pio = 1'b1; |
| 421 | |
| 422 | } |
| 423 | |
| 424 | |