1/* 2 * Copyright (c) 2015, 2017 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andreas Sandberg 38 */ 39 40#include "dev/pixelpump.hh" 41 42const DisplayTimings DisplayTimings::vga( 43 640, 480, 44 48, 96, 16, 45 33, 2, 10); 46 47 48DisplayTimings::DisplayTimings(unsigned _width, unsigned _height, 49 unsigned hbp, unsigned h_sync, unsigned hfp, 50 unsigned vbp, unsigned v_sync, unsigned vfp) 51 : width(_width), height(_height), 52 hBackPorch(hbp), hFrontPorch(hfp), hSync(h_sync), 53 vBackPorch(vbp), vFrontPorch(vfp), vSync(v_sync) 54{ 55} 56 57void 58DisplayTimings::serialize(CheckpointOut &cp) const 59{ 60 SERIALIZE_SCALAR(width); 61 SERIALIZE_SCALAR(height); 62 63 SERIALIZE_SCALAR(hBackPorch); 64 SERIALIZE_SCALAR(hFrontPorch); 65 SERIALIZE_SCALAR(hSync); 66 67 SERIALIZE_SCALAR(vBackPorch); 68 SERIALIZE_SCALAR(vFrontPorch); 69 SERIALIZE_SCALAR(vSync); 70} 71 72void 73DisplayTimings::unserialize(CheckpointIn &cp) 74{ 75 UNSERIALIZE_SCALAR(width); 76 UNSERIALIZE_SCALAR(height); 77 78 UNSERIALIZE_SCALAR(hBackPorch); 79 UNSERIALIZE_SCALAR(hFrontPorch); 80 UNSERIALIZE_SCALAR(hSync); 81 82 UNSERIALIZE_SCALAR(vBackPorch); 83 UNSERIALIZE_SCALAR(vFrontPorch); 84 UNSERIALIZE_SCALAR(vSync); 85} 86 87 88BasePixelPump::BasePixelPump(EventManager &em, ClockDomain &pxl_clk, 89 unsigned pixel_chunk) 90 : EventManager(em), Clocked(pxl_clk), Serializable(), 91 pixelChunk(pixel_chunk), 92 pixelEvents(), 93 evVSyncBegin("evVSyncBegin", this, &BasePixelPump::onVSyncBegin), 94 evVSyncEnd("evVSyncEnd", this, &BasePixelPump::onVSyncEnd), 95 evHSyncBegin("evHSyncBegin", this, &BasePixelPump::onHSyncBegin), 96 evHSyncEnd("evHSyncEnd", this, &BasePixelPump::onHSyncEnd), 97 evBeginLine("evBeginLine", this, &BasePixelPump::beginLine), 98 evRenderPixels("evRenderPixels", this, &BasePixelPump::renderPixels), 99 _timings(DisplayTimings::vga), 100 line(0), _posX(0), _underrun(false) 101{ 102} 103 104BasePixelPump::~BasePixelPump() 105{ 106} 107 108void 109BasePixelPump::serialize(CheckpointOut &cp) const 110{ 111 SERIALIZE_SCALAR(line); 112 SERIALIZE_SCALAR(_posX); 113 SERIALIZE_SCALAR(_underrun); 114 115 SERIALIZE_OBJ(_timings); 116 SERIALIZE_OBJ(fb); 117 118 for (PixelEvent *event : pixelEvents) 119 event->serializeSection(cp, event->name()); 120} 121 122void 123BasePixelPump::unserialize(CheckpointIn &cp) 124{ 125 UNSERIALIZE_SCALAR(line); 126 UNSERIALIZE_SCALAR(_posX); 127 UNSERIALIZE_SCALAR(_underrun); 128 129 UNSERIALIZE_OBJ(_timings); 130 UNSERIALIZE_OBJ(fb); 131 132 // We don't need to reschedule the event here since the event was 133 // suspended by PixelEvent::drain() and will be rescheduled by 134 // PixelEvent::drainResume(). 135 for (PixelEvent *event : pixelEvents) 136 event->unserializeSection(cp, event->name()); 137} 138 139void 140BasePixelPump::updateTimings(const DisplayTimings &timings) 141{ 142 panic_if(active(), "Trying to update timings in active PixelPump\n"); 143 144 _timings = timings; 145 146 // Resize the frame buffer if needed 147 if (_timings.width != fb.width() || _timings.height != fb.height()) 148 fb.resize(timings.width, timings.height); 149 150 // Set the current line past the last line in the frame. This 151 // triggers the new frame logic in beginLine(). 152 line = _timings.linesPerFrame(); 153} 154 155void 156BasePixelPump::start() 157{ 158 schedule(evBeginLine, clockEdge()); 159} 160 161 162void 163BasePixelPump::stop() 164{ 165 if (evVSyncEnd.scheduled()) 166 deschedule(evVSyncEnd); 167 168 if (evHSyncBegin.scheduled()) 169 deschedule(evHSyncBegin); 170 171 if (evHSyncEnd.scheduled()) 172 deschedule(evHSyncEnd); 173 174 if (evBeginLine.scheduled()) 175 deschedule(evBeginLine); 176 177 if (evRenderPixels.scheduled()) 178 deschedule(evRenderPixels); 179} 180 181void 182BasePixelPump::beginLine() 183{ 184 _posX = 0; 185 line++; 186 if (line >= _timings.linesPerFrame()) { 187 _underrun = false; 188 line = 0; 189 } 190 191 if (line == _timings.lineVSyncStart()) { 192 onVSyncBegin(); 193 } else if (line == _timings.lineVBackPorchStart()) { 194 onVSyncEnd(); 195 } 196 197 const Cycles h_sync_begin(0); 198 schedule(evHSyncBegin, clockEdge(h_sync_begin)); 199 200 const Cycles h_sync_end(h_sync_begin + _timings.hSync); 201 schedule(evHSyncEnd, clockEdge(h_sync_end)); 202 203 // Visible area 204 if (line >= _timings.lineFirstVisible() && 205 line < _timings.lineFrontPorchStart()) { 206 207 const Cycles h_first_visible(h_sync_end + _timings.hBackPorch); 208 schedule(evRenderPixels, clockEdge(h_first_visible)); 209 } 210 211 schedule(evBeginLine, clockEdge(_timings.cyclesPerLine())); 212} 213 214void 215BasePixelPump::renderPixels() 216{ 217 // Try to handle multiple pixels at a time; doing so reduces the 218 // accuracy of the underrun detection but lowers simulation 219 // overhead 220 const unsigned x_end(std::min(_posX + pixelChunk, _timings.width)); 221 const unsigned pxl_count(x_end - _posX); 222 const unsigned pos_y(posY()); 223 224 Pixel pixel(0, 0, 0); 225 const Pixel underrun_pixel(0, 0, 0); 226 for (; _posX < x_end && !_underrun; ++_posX) { 227 if (!nextPixel(pixel)) { 228 warn("Input buffer underrun in BasePixelPump (%u, %u)\n", 229 _posX, pos_y); 230 _underrun = true; 231 onUnderrun(_posX, pos_y); 232 pixel = underrun_pixel; 233 } 234 fb.pixel(_posX, pos_y) = pixel; 235 } 236 237 // Fill remaining pixels with a dummy pixel value if we ran out of 238 // data 239 for (; _posX < x_end; ++_posX) 240 fb.pixel(_posX, pos_y) = underrun_pixel; 241 242 // Schedule a new event to handle the next block of pixels 243 if (_posX < _timings.width) { 244 schedule(evRenderPixels, clockEdge(Cycles(pxl_count))); 245 } else { 246 if (pos_y == _timings.height - 1) 247 onFrameDone(); 248 } 249} 250 251void 252BasePixelPump::renderFrame() 253{ 254 _underrun = false; 255 line = 0; 256 257 // Signal vsync end and render the frame 258 line = _timings.lineVBackPorchStart(); 259 onVSyncEnd(); 260 261 // We only care about the visible screen area when rendering the 262 // frame 263 for (line = _timings.lineFirstVisible(); 264 line < _timings.lineFrontPorchStart(); 265 ++line) { 266 267 _posX = 0; 268 269 onHSyncBegin(); 270 onHSyncEnd(); 271 272 renderLine(); 273 } 274 275 line = _timings.lineFrontPorchStart() - 1; 276 onFrameDone(); 277 278 // Signal vsync until the next frame begins 279 line = _timings.lineVSyncStart(); 280 onVSyncBegin(); 281} 282 283void 284BasePixelPump::renderLine() 285{ 286 const unsigned pos_y(posY()); 287 288 Pixel pixel(0, 0, 0); 289 for (_posX = 0; _posX < _timings.width; ++_posX) { 290 if (!nextPixel(pixel)) { 291 panic("Unexpected underrun in BasePixelPump (%u, %u)\n", 292 _posX, pos_y); 293 } 294 fb.pixel(_posX, pos_y) = pixel; 295 } 296} 297 298 299BasePixelPump::PixelEvent::PixelEvent( 300 const char *name, BasePixelPump *_parent, CallbackType _func) 301 : Event(), Drainable(), 302 _name(name), parent(*_parent), func(_func), 303 suspended(false), 304 relativeTick(0) 305{ 306 parent.pixelEvents.push_back(this); 307} 308 309DrainState 310BasePixelPump::PixelEvent::drain() 311{ 312 if (scheduled()) 313 suspend(); 314 return DrainState::Drained; 315} 316 317void 318BasePixelPump::PixelEvent::drainResume() 319{ 320 if (suspended) 321 resume(); 322} 323 324void 325BasePixelPump::PixelEvent::serialize(CheckpointOut &cp) const 326{ 327 assert(!scheduled()); 328 Event::serialize(cp); 329 SERIALIZE_SCALAR(suspended); 330 SERIALIZE_SCALAR(relativeTick); 331} 332 333void 334BasePixelPump::PixelEvent::unserialize(CheckpointIn &cp) 335{ 336 Event::unserialize(cp); 337 UNSERIALIZE_SCALAR(suspended); 338 UNSERIALIZE_SCALAR(relativeTick); 339 assert(!scheduled()); 340} 341 342void 343BasePixelPump::PixelEvent::suspend() 344{ 345 assert(scheduled()); 346 assert(!suspended); 347 348 suspended = true; 349 relativeTick = when() - curTick(); 350 parent.deschedule(this); 351} 352 353void 354BasePixelPump::PixelEvent::resume() 355{ 356 assert(!scheduled()); 357 assert(suspended); 358 parent.schedule(this, relativeTick + curTick()); 359 suspended = false; 360 relativeTick = 0; 361} 362