base.cc revision 56
1/* 2 * Copyright (c) 2003 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <iostream> 30#include <iomanip> 31#include <list> 32#include <sstream> 33#include <string> 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <math.h> 38 39#include "sim/host.hh" 40#include "base/cprintf.hh" 41#include "base/misc.hh" 42#include "cpu/full_cpu/smt.hh" 43 44#include "sim/annotation.hh" 45#include "cpu/exec_context.hh" 46#include "cpu/base_cpu.hh" 47#include "sim/debug.hh" 48#include "cpu/simple_cpu/simple_cpu.hh" 49#include "base/inifile.hh" 50#include "mem/mem_interface.hh" 51#include "mem/base_mem.hh" 52#include "cpu/static_inst.hh" 53 54#ifdef FULL_SYSTEM 55#include "mem/functional_mem/memory_control.hh" 56#include "mem/functional_mem/physical_memory.hh" 57#include "targetarch/alpha_memory.hh" 58#include "sim/system.hh" 59#else // !FULL_SYSTEM 60#include "mem/functional_mem/functional_memory.hh" 61#include "sim/prog.hh" 62#include "eio/eio.hh" 63#endif // FULL_SYSTEM 64 65#include "cpu/exetrace.hh" 66#include "base/trace.hh" 67#include "sim/sim_events.hh" 68#include "base/pollevent.hh" 69#include "sim/sim_object.hh" 70#include "sim/sim_stats.hh" 71 72#include "base/range.hh" 73#include "base/loader/symtab.hh" 74 75#ifdef FULL_SYSTEM 76#include "targetarch/vtophys.hh" 77#include "dev/pciareg.h" 78#include "base/remote_gdb.hh" 79#include "dev/alpha_access.h" 80#endif 81 82 83using namespace std; 84 85SimpleCPU::CacheCompletionEvent::CacheCompletionEvent(SimpleCPU *_cpu) 86 : Event(&mainEventQueue), 87 cpu(_cpu) 88{ 89} 90 91void SimpleCPU::CacheCompletionEvent::process() 92{ 93 cpu->processCacheCompletion(); 94} 95 96const char * 97SimpleCPU::CacheCompletionEvent::description() 98{ 99 return "cache completion event"; 100} 101 102#ifdef FULL_SYSTEM 103SimpleCPU::SimpleCPU(const string &_name, 104 System *_system, 105 Counter max_insts_any_thread, 106 Counter max_insts_all_threads, 107 AlphaItb *itb, AlphaDtb *dtb, 108 FunctionalMemory *mem, 109 MemInterface *icache_interface, 110 MemInterface *dcache_interface, 111 int cpu_id, Tick freq) 112 : BaseCPU(_name, /* number_of_threads */ 1, 113 max_insts_any_thread, max_insts_all_threads, 114 _system, cpu_id, freq), 115#else 116SimpleCPU::SimpleCPU(const string &_name, Process *_process, 117 Counter max_insts_any_thread, 118 Counter max_insts_all_threads, 119 MemInterface *icache_interface, 120 MemInterface *dcache_interface) 121 : BaseCPU(_name, /* number_of_threads */ 1, 122 max_insts_any_thread, max_insts_all_threads), 123#endif 124 tickEvent(this), xc(NULL), cacheCompletionEvent(this) 125{ 126#ifdef FULL_SYSTEM 127 xc = new ExecContext(this, 0, system, itb, dtb, mem, cpu_id); 128 129 _status = Running; 130 if (cpu_id != 0) { 131 132 xc->setStatus(ExecContext::Unallocated); 133 134 //Open a GDB debug session on port (7000 + the cpu_id) 135 (new GDBListener(new RemoteGDB(system, xc), 7000 + cpu_id))->listen(); 136 137 AlphaISA::init(system->physmem, &xc->regs); 138 139 fault = Reset_Fault; 140 141 IntReg *ipr = xc->regs.ipr; 142 ipr[TheISA::IPR_MCSR] = 0x6; 143 144 AlphaISA::swap_palshadow(&xc->regs, true); 145 146 xc->regs.pc = 147 ipr[TheISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault]; 148 xc->regs.npc = xc->regs.pc + sizeof(MachInst); 149 150 _status = Idle; 151 } 152 else { 153 system->initBootContext(xc); 154 155 // Reset the system 156 // 157 AlphaISA::init(system->physmem, &xc->regs); 158 159 fault = Reset_Fault; 160 161 IntReg *ipr = xc->regs.ipr; 162 ipr[TheISA::IPR_MCSR] = 0x6; 163 164 AlphaISA::swap_palshadow(&xc->regs, true); 165 166 xc->regs.pc = ipr[TheISA::IPR_PAL_BASE] + AlphaISA::fault_addr[fault]; 167 xc->regs.npc = xc->regs.pc + sizeof(MachInst); 168 169 _status = Running; 170 tickEvent.schedule(0); 171 } 172 173#else 174 xc = new ExecContext(this, /* thread_num */ 0, _process, /* asid */ 0); 175 fault = No_Fault; 176 if (xc->status() == ExecContext::Active) { 177 _status = Running; 178 tickEvent.schedule(0); 179 } else 180 _status = Idle; 181#endif // !FULL_SYSTEM 182 183 icacheInterface = icache_interface; 184 dcacheInterface = dcache_interface; 185 186 memReq = new MemReq(); 187 memReq->xc = xc; 188 memReq->asid = 0; 189 190 numInst = 0; 191 last_idle = 0; 192 lastIcacheStall = 0; 193 lastDcacheStall = 0; 194 195 contexts.push_back(xc); 196} 197 198SimpleCPU::~SimpleCPU() 199{ 200} 201 202void 203SimpleCPU::regStats() 204{ 205 BaseCPU::regStats(); 206 207 numInsts 208 .name(name() + ".num_insts") 209 .desc("Number of instructions executed") 210 ; 211 212 numMemRefs 213 .name(name() + ".num_refs") 214 .desc("Number of memory references") 215 ; 216 217 idleCycles 218 .name(name() + ".idle_cycles") 219 .desc("Number of idle cycles") 220 ; 221 222 idleFraction 223 .name(name() + ".idle_fraction") 224 .desc("Percentage of idle cycles") 225 ; 226 227 icacheStallCycles 228 .name(name() + ".icache_stall_cycles") 229 .desc("ICache total stall cycles") 230 .prereq(icacheStallCycles) 231 ; 232 233 dcacheStallCycles 234 .name(name() + ".dcache_stall_cycles") 235 .desc("DCache total stall cycles") 236 .prereq(dcacheStallCycles) 237 ; 238 239 idleFraction = idleCycles / simTicks; 240 241 numInsts = Statistics::scalar(numInst); 242 simInsts += numInsts; 243} 244 245void 246SimpleCPU::serialize() 247{ 248 nameOut(); 249 250#ifdef FULL_SYSTEM 251#if 0 252 // do we need this anymore?? egh 253 childOut("itb", xc->itb); 254 childOut("dtb", xc->dtb); 255 childOut("physmem", physmem); 256#endif 257#endif 258 259 for (int i = 0; i < NumIntRegs; i++) { 260 stringstream buf; 261 ccprintf(buf, "R%02d", i); 262 paramOut(buf.str(), xc->regs.intRegFile[i]); 263 } 264 for (int i = 0; i < NumFloatRegs; i++) { 265 stringstream buf; 266 ccprintf(buf, "F%02d", i); 267 paramOut(buf.str(), xc->regs.floatRegFile.d[i]); 268 } 269 // CPUTraitsType::serializeSpecialRegs(getProxy(), xc->regs); 270} 271 272void 273SimpleCPU::unserialize(IniFile &db, const string &category, ConfigNode *node) 274{ 275 string data; 276 277 for (int i = 0; i < NumIntRegs; i++) { 278 stringstream buf; 279 ccprintf(buf, "R%02d", i); 280 db.findDefault(category, buf.str(), data); 281 to_number(data,xc->regs.intRegFile[i]); 282 } 283 for (int i = 0; i < NumFloatRegs; i++) { 284 stringstream buf; 285 ccprintf(buf, "F%02d", i); 286 db.findDefault(category, buf.str(), data); 287 xc->regs.floatRegFile.d[i] = strtod(data.c_str(),NULL); 288 } 289 290 // Read in Special registers 291 292 // CPUTraitsType::unserializeSpecialRegs(db,category,node,xc->regs); 293} 294 295void 296change_thread_state(int thread_number, int activate, int priority) 297{ 298} 299 300// precise architected memory state accessor macros 301template <class T> 302Fault 303SimpleCPU::read(Addr addr, T& data, unsigned flags) 304{ 305 memReq->reset(addr, sizeof(T), flags); 306 307 // translate to physical address 308 Fault fault = xc->translateDataReadReq(memReq); 309 310 // do functional access 311 if (fault == No_Fault) 312 fault = xc->read(memReq, data); 313 314 if (traceData) { 315 traceData->setAddr(addr); 316 if (fault == No_Fault) 317 traceData->setData(data); 318 } 319 320 // if we have a cache, do cache access too 321 if (fault == No_Fault && dcacheInterface) { 322 memReq->cmd = Read; 323 memReq->completionEvent = NULL; 324 memReq->time = curTick; 325 memReq->flags &= ~UNCACHEABLE; 326 MemAccessResult result = dcacheInterface->access(memReq); 327 328 // Ugly hack to get an event scheduled *only* if the access is 329 // a miss. We really should add first-class support for this 330 // at some point. 331 if (result != MA_HIT && dcacheInterface->doEvents) { 332 memReq->completionEvent = &cacheCompletionEvent; 333 setStatus(DcacheMissStall); 334 } 335 } 336 337 return fault; 338} 339 340#ifndef DOXYGEN_SHOULD_SKIP_THIS 341 342template 343Fault 344SimpleCPU::read(Addr addr, uint64_t& data, unsigned flags); 345 346template 347Fault 348SimpleCPU::read(Addr addr, uint32_t& data, unsigned flags); 349 350template 351Fault 352SimpleCPU::read(Addr addr, uint16_t& data, unsigned flags); 353 354template 355Fault 356SimpleCPU::read(Addr addr, uint8_t& data, unsigned flags); 357 358#endif //DOXYGEN_SHOULD_SKIP_THIS 359 360template<> 361Fault 362SimpleCPU::read(Addr addr, double& data, unsigned flags) 363{ 364 return read(addr, *(uint64_t*)&data, flags); 365} 366 367template<> 368Fault 369SimpleCPU::read(Addr addr, float& data, unsigned flags) 370{ 371 return read(addr, *(uint32_t*)&data, flags); 372} 373 374 375template<> 376Fault 377SimpleCPU::read(Addr addr, int32_t& data, unsigned flags) 378{ 379 return read(addr, (uint32_t&)data, flags); 380} 381 382 383template <class T> 384Fault 385SimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) 386{ 387 if (traceData) { 388 traceData->setAddr(addr); 389 traceData->setData(data); 390 } 391 392 memReq->reset(addr, sizeof(T), flags); 393 394 // translate to physical address 395 Fault fault = xc->translateDataWriteReq(memReq); 396 397 // do functional access 398 if (fault == No_Fault) 399 fault = xc->write(memReq, data); 400 401 if (fault == No_Fault && dcacheInterface) { 402 memReq->cmd = Write; 403 memReq->data = (uint8_t *)&data; 404 memReq->completionEvent = NULL; 405 memReq->time = curTick; 406 memReq->flags &= ~UNCACHEABLE; 407 MemAccessResult result = dcacheInterface->access(memReq); 408 409 // Ugly hack to get an event scheduled *only* if the access is 410 // a miss. We really should add first-class support for this 411 // at some point. 412 if (result != MA_HIT && dcacheInterface->doEvents) { 413 memReq->completionEvent = &cacheCompletionEvent; 414 setStatus(DcacheMissStall); 415 } 416 } 417 418 if (res && (fault == No_Fault)) 419 *res = memReq->result; 420 421 return fault; 422} 423 424 425#ifndef DOXYGEN_SHOULD_SKIP_THIS 426template 427Fault 428SimpleCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); 429 430template 431Fault 432SimpleCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); 433 434template 435Fault 436SimpleCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); 437 438template 439Fault 440SimpleCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); 441 442#endif //DOXYGEN_SHOULD_SKIP_THIS 443 444template<> 445Fault 446SimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) 447{ 448 return write(*(uint64_t*)&data, addr, flags, res); 449} 450 451template<> 452Fault 453SimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) 454{ 455 return write(*(uint32_t*)&data, addr, flags, res); 456} 457 458 459template<> 460Fault 461SimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) 462{ 463 return write((uint32_t)data, addr, flags, res); 464} 465 466 467#ifdef FULL_SYSTEM 468Addr 469SimpleCPU::dbg_vtophys(Addr addr) 470{ 471 return vtophys(xc, addr); 472} 473#endif // FULL_SYSTEM 474 475Tick save_cycle = 0; 476 477 478void 479SimpleCPU::processCacheCompletion() 480{ 481 switch (status()) { 482 case IcacheMissStall: 483 icacheStallCycles += curTick - lastIcacheStall; 484 setStatus(IcacheMissComplete); 485 break; 486 case DcacheMissStall: 487 dcacheStallCycles += curTick - lastDcacheStall; 488 setStatus(Running); 489 break; 490 default: 491 panic("SimpleCPU::processCacheCompletion: bad state"); 492 break; 493 } 494} 495 496#ifdef FULL_SYSTEM 497void 498SimpleCPU::post_interrupt(int int_num, int index) 499{ 500 BaseCPU::post_interrupt(int_num, index); 501 502 if (xc->status() == ExecContext::Suspended) { 503 DPRINTF(IPI,"Suspended Processor awoke\n"); 504 xc->setStatus(ExecContext::Active); 505 Annotate::Resume(xc); 506 } 507} 508#endif // FULL_SYSTEM 509 510/* start simulation, program loaded, processor precise state initialized */ 511void 512SimpleCPU::tick() 513{ 514 traceData = NULL; 515 516#ifdef FULL_SYSTEM 517 if (fault == No_Fault && AlphaISA::check_interrupts && 518 xc->cpu->check_interrupts() && 519 !PC_PAL(xc->regs.pc) && 520 status() != IcacheMissComplete) { 521 int ipl = 0; 522 int summary = 0; 523 AlphaISA::check_interrupts = 0; 524 IntReg *ipr = xc->regs.ipr; 525 526 if (xc->regs.ipr[TheISA::IPR_SIRR]) { 527 for (int i = TheISA::INTLEVEL_SOFTWARE_MIN; 528 i < TheISA::INTLEVEL_SOFTWARE_MAX; i++) { 529 if (ipr[TheISA::IPR_SIRR] & (ULL(1) << i)) { 530 // See table 4-19 of 21164 hardware reference 531 ipl = (i - TheISA::INTLEVEL_SOFTWARE_MIN) + 1; 532 summary |= (ULL(1) << i); 533 } 534 } 535 } 536 537 uint64_t interrupts = xc->cpu->intr_status(); 538 for(int i = TheISA::INTLEVEL_EXTERNAL_MIN; 539 i < TheISA::INTLEVEL_EXTERNAL_MAX; i++) { 540 if (interrupts & (ULL(1) << i)) { 541 // See table 4-19 of 21164 hardware reference 542 ipl = i; 543 summary |= (ULL(1) << i); 544 } 545 } 546 547 if (ipr[TheISA::IPR_ASTRR]) 548 panic("asynchronous traps not implemented\n"); 549 550 if (ipl && ipl > xc->regs.ipr[TheISA::IPR_IPLR]) { 551 ipr[TheISA::IPR_ISR] = summary; 552 ipr[TheISA::IPR_INTID] = ipl; 553 xc->ev5_trap(Interrupt_Fault); 554 555 DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", 556 ipr[TheISA::IPR_IPLR], ipl, summary); 557 } 558 } 559#endif 560 561 // maintain $r0 semantics 562 xc->regs.intRegFile[ZeroReg] = 0; 563#ifdef TARGET_ALPHA 564 xc->regs.floatRegFile.d[ZeroReg] = 0.0; 565#endif // TARGET_ALPHA 566 567 if (status() == IcacheMissComplete) { 568 // We've already fetched an instruction and were stalled on an 569 // I-cache miss. No need to fetch it again. 570 571 setStatus(Running); 572 } 573 else { 574 // Try to fetch an instruction 575 576 // set up memory request for instruction fetch 577#ifdef FULL_SYSTEM 578#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 579#else 580#define IFETCH_FLAGS(pc) 0 581#endif 582 583 memReq->cmd = Read; 584 memReq->reset(xc->regs.pc & ~3, sizeof(uint32_t), 585 IFETCH_FLAGS(xc->regs.pc)); 586 587 fault = xc->translateInstReq(memReq); 588 589 if (fault == No_Fault) 590 fault = xc->mem->read(memReq, inst); 591 592 if (icacheInterface && fault == No_Fault) { 593 memReq->completionEvent = NULL; 594 595 memReq->time = curTick; 596 memReq->flags &= ~UNCACHEABLE; 597 MemAccessResult result = icacheInterface->access(memReq); 598 599 // Ugly hack to get an event scheduled *only* if the access is 600 // a miss. We really should add first-class support for this 601 // at some point. 602 if (result != MA_HIT && icacheInterface->doEvents) { 603 memReq->completionEvent = &cacheCompletionEvent; 604 setStatus(IcacheMissStall); 605 return; 606 } 607 } 608 } 609 610 // If we've got a valid instruction (i.e., no fault on instruction 611 // fetch), then execute it. 612 if (fault == No_Fault) { 613 614 // keep an instruction count 615 numInst++; 616 617 // check for instruction-count-based events 618 comInsnEventQueue[0]->serviceEvents(numInst); 619 620 // decode the instruction 621 StaticInstPtr<TheISA> si(inst); 622 623 traceData = Trace::getInstRecord(curTick, xc, this, si, 624 xc->regs.pc); 625 626#ifdef FULL_SYSTEM 627 xc->regs.opcode = (inst >> 26) & 0x3f; 628 xc->regs.ra = (inst >> 21) & 0x1f; 629#endif // FULL_SYSTEM 630 631 xc->func_exe_insn++; 632 633 fault = si->execute(this, xc, traceData); 634 635 if (si->isMemRef()) { 636 numMemRefs++; 637 } 638 639 if (traceData) 640 traceData->finalize(); 641 642 } // if (fault == No_Fault) 643 644 if (fault != No_Fault) { 645#ifdef FULL_SYSTEM 646 xc->ev5_trap(fault); 647#else // !FULL_SYSTEM 648 fatal("fault (%d) detected @ PC 0x%08p", fault, xc->regs.pc); 649#endif // FULL_SYSTEM 650 } 651 else { 652 // go to the next instruction 653 xc->regs.pc = xc->regs.npc; 654 xc->regs.npc += sizeof(MachInst); 655 } 656 657#ifdef FULL_SYSTEM 658 Addr oldpc; 659 do { 660 oldpc = xc->regs.pc; 661 system->pcEventQueue.service(xc); 662 } while (oldpc != xc->regs.pc); 663#endif 664 665 assert(status() == Running || 666 status() == Idle || 667 status() == DcacheMissStall); 668 669 if (status() == Running && !tickEvent.scheduled()) 670 tickEvent.schedule(curTick + 1); 671} 672 673 674//////////////////////////////////////////////////////////////////////// 675// 676// SimpleCPU Simulation Object 677// 678BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) 679 680 Param<Counter> max_insts_any_thread; 681 Param<Counter> max_insts_all_threads; 682 683#ifdef FULL_SYSTEM 684 SimObjectParam<AlphaItb *> itb; 685 SimObjectParam<AlphaDtb *> dtb; 686 SimObjectParam<FunctionalMemory *> mem; 687 SimObjectParam<System *> system; 688 Param<int> cpu_id; 689 Param<int> mult; 690#else 691 SimObjectParam<Process *> workload; 692#endif // FULL_SYSTEM 693 694 SimObjectParam<BaseMem *> icache; 695 SimObjectParam<BaseMem *> dcache; 696 697END_DECLARE_SIM_OBJECT_PARAMS(SimpleCPU) 698 699BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleCPU) 700 701 INIT_PARAM_DFLT(max_insts_any_thread, 702 "terminate when any thread reaches this insn count", 703 0), 704 INIT_PARAM_DFLT(max_insts_all_threads, 705 "terminate when all threads have reached this insn count", 706 0), 707 708#ifdef FULL_SYSTEM 709 INIT_PARAM(itb, "Instruction TLB"), 710 INIT_PARAM(dtb, "Data TLB"), 711 INIT_PARAM(mem, "memory"), 712 INIT_PARAM(system, "system object"), 713 INIT_PARAM_DFLT(cpu_id, "CPU identification number", 0), 714 INIT_PARAM_DFLT(mult, "system clock multiplier", 1), 715#else 716 INIT_PARAM(workload, "processes to run"), 717#endif // FULL_SYSTEM 718 719 INIT_PARAM_DFLT(icache, "L1 instruction cache object", NULL), 720 INIT_PARAM_DFLT(dcache, "L1 data cache object", NULL) 721 722END_INIT_SIM_OBJECT_PARAMS(SimpleCPU) 723 724 725CREATE_SIM_OBJECT(SimpleCPU) 726{ 727#ifdef FULL_SYSTEM 728 if (mult != 1) 729 panic("processor clock multiplier must be 1\n"); 730 731 return new SimpleCPU(getInstanceName(), system, 732 max_insts_any_thread, max_insts_all_threads, 733 itb, dtb, mem, 734 (icache) ? icache->getInterface() : NULL, 735 (dcache) ? dcache->getInterface() : NULL, 736 cpu_id, ticksPerSecond * mult); 737#else 738 739 return new SimpleCPU(getInstanceName(), workload, 740 max_insts_any_thread, max_insts_all_threads, 741 icache->getInterface(), dcache->getInterface()); 742 743#endif // FULL_SYSTEM 744} 745 746REGISTER_SIM_OBJECT("SimpleCPU", SimpleCPU) 747