diff options
-rw-r--r-- | src/dev/SConscript | 1 | ||||
-rw-r--r-- | src/dev/pixelpump.cc | 306 | ||||
-rw-r--r-- | src/dev/pixelpump.hh | 312 |
3 files changed, 619 insertions, 0 deletions
diff --git a/src/dev/SConscript b/src/dev/SConscript index 3936210a6..cd99e984a 100644 --- a/src/dev/SConscript +++ b/src/dev/SConscript @@ -75,6 +75,7 @@ Source('mc146818.cc') Source('ns_gige.cc') Source('pciconfigall.cc') Source('pcidev.cc') +Source('pixelpump.cc') Source('pktfifo.cc') Source('platform.cc') Source('ps2.cc') diff --git a/src/dev/pixelpump.cc b/src/dev/pixelpump.cc new file mode 100644 index 000000000..3ffe96d96 --- /dev/null +++ b/src/dev/pixelpump.cc @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Sandberg + */ + +#include "dev/pixelpump.hh" + +const DisplayTimings DisplayTimings::vga( + 640, 480, + 48, 96, 16, + 33, 2, 10); + + +DisplayTimings::DisplayTimings(unsigned _width, unsigned _height, + unsigned hbp, unsigned h_sync, unsigned hfp, + unsigned vbp, unsigned v_sync, unsigned vfp) + : width(_width), height(_height), + hBackPorch(hbp), hFrontPorch(hfp), hSync(h_sync), + vBackPorch(vbp), vFrontPorch(vfp), vSync(v_sync) +{ +} + +void +DisplayTimings::serialize(CheckpointOut &cp) const +{ + SERIALIZE_SCALAR(width); + SERIALIZE_SCALAR(height); + + SERIALIZE_SCALAR(hBackPorch); + SERIALIZE_SCALAR(hFrontPorch); + SERIALIZE_SCALAR(hSync); + + SERIALIZE_SCALAR(vBackPorch); + SERIALIZE_SCALAR(vFrontPorch); + SERIALIZE_SCALAR(vSync); +} + +void +DisplayTimings::unserialize(CheckpointIn &cp) +{ + UNSERIALIZE_SCALAR(width); + UNSERIALIZE_SCALAR(height); + + UNSERIALIZE_SCALAR(hBackPorch); + UNSERIALIZE_SCALAR(hFrontPorch); + UNSERIALIZE_SCALAR(hSync); + + UNSERIALIZE_SCALAR(vBackPorch); + UNSERIALIZE_SCALAR(vFrontPorch); + UNSERIALIZE_SCALAR(vSync); +} + + +BasePixelPump::BasePixelPump(EventManager &em, ClockDomain &pxl_clk, + unsigned pixel_chunk) + : EventManager(em), Clocked(pxl_clk), Serializable(), + pixelChunk(pixel_chunk), + pixelEvents(), + evVSyncBegin("evVSyncBegin", this, &BasePixelPump::onVSyncBegin), + evVSyncEnd("evVSyncEnd", this, &BasePixelPump::onVSyncEnd), + evHSyncBegin("evHSyncBegin", this, &BasePixelPump::onHSyncBegin), + evHSyncEnd("evHSyncEnd", this, &BasePixelPump::onHSyncEnd), + evBeginLine("evBeginLine", this, &BasePixelPump::beginLine), + evRenderPixels("evRenderPixels", this, &BasePixelPump::renderPixels), + _timings(DisplayTimings::vga), + line(0), _posX(0), _underrun(false) +{ +} + +BasePixelPump::~BasePixelPump() +{ +} + +void +BasePixelPump::serialize(CheckpointOut &cp) const +{ + SERIALIZE_SCALAR(line); + SERIALIZE_SCALAR(_posX); + SERIALIZE_SCALAR(_underrun); + + SERIALIZE_OBJ(_timings); + SERIALIZE_OBJ(fb); + + for (PixelEvent *event : pixelEvents) + event->serializeSection(cp, event->name()); +} + +void +BasePixelPump::unserialize(CheckpointIn &cp) +{ + UNSERIALIZE_SCALAR(line); + UNSERIALIZE_SCALAR(_posX); + UNSERIALIZE_SCALAR(_underrun); + + UNSERIALIZE_OBJ(_timings); + UNSERIALIZE_OBJ(fb); + + // We don't need to reschedule the event here since the event was + // suspended by PixelEvent::drain() and will be rescheduled by + // PixelEvent::drainResume(). + for (PixelEvent *event : pixelEvents) + event->unserializeSection(cp, event->name()); +} + + +void +BasePixelPump::start(const DisplayTimings &timings) +{ + _timings = timings; + + // Resize the frame buffer if needed + if (_timings.width != fb.width() || _timings.height != fb.height()) + fb.resize(timings.width, timings.height); + + // Set the current line past the last line in the frame. This + // triggers the new frame logic in beginLine(). + line = _timings.linesPerFrame(); + schedule(evBeginLine, clockEdge()); +} + +void +BasePixelPump::stop() +{ + if (evVSyncEnd.scheduled()) + deschedule(evVSyncEnd); + + if (evHSyncBegin.scheduled()) + deschedule(evHSyncBegin); + + if (evHSyncEnd.scheduled()) + deschedule(evHSyncEnd); + + if (evBeginLine.scheduled()) + deschedule(evBeginLine); + + if (evRenderPixels.scheduled()) + deschedule(evRenderPixels); +} + +void +BasePixelPump::beginLine() +{ + _posX = 0; + line++; + if (line >= _timings.linesPerFrame()) { + _underrun = false; + line = 0; + } + + if (line == _timings.lineVSyncStart()) { + onVSyncBegin(); + } else if (line == _timings.lineVBackPorchStart()) { + onVSyncEnd(); + } + + const Cycles h_sync_begin(0); + schedule(evHSyncBegin, clockEdge(h_sync_begin)); + + const Cycles h_sync_end(h_sync_begin + _timings.hSync); + schedule(evHSyncEnd, clockEdge(h_sync_end)); + + // Visible area + if (line >= _timings.lineFirstVisible() && + line < _timings.lineFrontPorchStart()) { + + const Cycles h_first_visible(h_sync_end + _timings.hBackPorch); + schedule(evRenderPixels, clockEdge(h_first_visible)); + } + + schedule(evBeginLine, clockEdge(_timings.cyclesPerLine())); +} + +void +BasePixelPump::renderPixels() +{ + // Try to handle multiple pixels at a time; doing so reduces the + // accuracy of the underrun detection but lowers simulation + // overhead + const unsigned x_end(std::min(_posX + pixelChunk, _timings.width)); + const unsigned pxl_count(x_end - _posX); + const unsigned pos_y(posY()); + + Pixel pixel(0, 0, 0); + const Pixel underrun_pixel(0, 0, 0); + for (; _posX < x_end && !_underrun; ++_posX) { + if (!nextPixel(pixel)) { + warn("Input buffer underrun in BasePixelPump (%u, %u)\n", + _posX, pos_y); + _underrun = true; + onUnderrun(_posX, pos_y); + pixel = underrun_pixel; + } + fb.pixel(_posX, pos_y) = pixel; + } + + // Fill remaining pixels with a dummy pixel value if we ran out of + // data + for (; _posX < x_end; ++_posX) + fb.pixel(_posX, pos_y) = underrun_pixel; + + // Schedule a new event to handle the next block of pixels + if (_posX < _timings.width) { + schedule(evRenderPixels, clockEdge(Cycles(pxl_count))); + } else { + if (pos_y == _timings.height - 1) + onFrameDone(); + } +} + +BasePixelPump::PixelEvent::PixelEvent( + const char *name, BasePixelPump *_parent, CallbackType _func) + : Event(), Drainable(), + _name(name), parent(*_parent), func(_func), + suspended(false), + relativeTick(0) +{ + parent.pixelEvents.push_back(this); +} + +DrainState +BasePixelPump::PixelEvent::drain() +{ + if (scheduled()) + suspend(); + return DrainState::Drained; +} + +void +BasePixelPump::PixelEvent::drainResume() +{ + if (suspended) + resume(); +} + +void +BasePixelPump::PixelEvent::serialize(CheckpointOut &cp) const +{ + assert(!scheduled()); + Event::serialize(cp); + SERIALIZE_SCALAR(suspended); + SERIALIZE_SCALAR(relativeTick); +} + +void +BasePixelPump::PixelEvent::unserialize(CheckpointIn &cp) +{ + Event::unserialize(cp); + UNSERIALIZE_SCALAR(suspended); + UNSERIALIZE_SCALAR(relativeTick); + assert(!scheduled()); +} + +void +BasePixelPump::PixelEvent::suspend() +{ + assert(scheduled()); + assert(!suspended); + + suspended = true; + relativeTick = when() - curTick(); + parent.deschedule(this); +} + +void +BasePixelPump::PixelEvent::resume() +{ + assert(!scheduled()); + assert(suspended); + parent.schedule(this, relativeTick + curTick()); + suspended = false; + relativeTick = 0; +} diff --git a/src/dev/pixelpump.hh b/src/dev/pixelpump.hh new file mode 100644 index 000000000..582e1aa18 --- /dev/null +++ b/src/dev/pixelpump.hh @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Sandberg + */ + +#ifndef __DEV_PIXELPUMP_HH__ +#define __DEV_PIXELPUMP_HH__ + +#include "base/framebuffer.hh" +#include "sim/clocked_object.hh" + +struct BasePixelPumpParams; + +struct DisplayTimings : public Serializable +{ + /** + * Create a display timing configuration struct + * + * @param width Width of the visible area of the screen. + * @param height Height of the visible area of the screen. + * @param hfp Horizontal front porch in pixel clocks. + * @param h_sync Horizontal sync in pixel clocks. + * @param hbp Horizontal back porch in pixel clocks. + * @param vfp Vertical front porch in scan lines. + * @param v_sync Vertical sync in scan lines. + * @param vbp Vertical back porch in scan lines. + */ + DisplayTimings(unsigned width, unsigned height, + unsigned hbp, unsigned h_sync, unsigned hfp, + unsigned vbp, unsigned v_sync, unsigned vfp); + + void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE; + void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; + + /** How many pixel clocks are required for one line? */ + Cycles cyclesPerLine() const { + return Cycles(hSync + hBackPorch + width + hBackPorch); + } + + /** How many pixel clocks are required for one frame? */ + Cycles cyclesPerFrame() const { + return Cycles(cyclesPerLine() * linesPerFrame()); + } + + /** Calculate the first line of the vsync signal */ + unsigned lineVSyncStart() const { + return 0; + } + + /** Calculate the first line of the vertical back porch */ + unsigned lineVBackPorchStart() const { + return lineVSyncStart() + vSync; + } + + /** Calculate the first line of the visible region */ + unsigned lineFirstVisible() const { + return lineVBackPorchStart() + vBackPorch; + } + + /** Calculate the first line of the back porch */ + unsigned lineFrontPorchStart() const { + return lineFirstVisible() + height; + } + + /** Calculate the total number of lines in a frame */ + unsigned linesPerFrame() const { + return lineFrontPorchStart() + vFrontPorch; + } + + /** Display width in pixels */ + unsigned width; + /** Display height in pixels */ + unsigned height; + + /** Horizontal back porch in pixels */ + unsigned hBackPorch; + /** Horizontal front porch in pixels */ + unsigned hFrontPorch; + /** Horizontal sync signal length in pixels */ + unsigned hSync; + + /** Vertical back porch in lines */ + unsigned vBackPorch; + /** Vertical front porch in lines */ + unsigned vFrontPorch; + /** Vertical sync signal in lines */ + unsigned vSync; + + static const DisplayTimings vga; +}; + +/** + * Timing generator for a pixel-based display. + * + * Pixels are ordered relative to the top left corner of the + * display. Scan lines appear in the following order: + * <ol> + * <li>Vertical Sync (starting at line 0) + * <li>Vertical back porch + * <li>Visible lines + * <li>Vertical front porch + * </ol> + * + * Pixel order within a scan line: + * <ol> + * <li>Horizontal Sync + * <li>Horizontal Back Porch + * <li>Visible pixels + * <li>Horizontal Front Porch + * </ol> + */ +class BasePixelPump + : public EventManager, public Clocked, + public Serializable +{ + public: + BasePixelPump(EventManager &em, ClockDomain &pxl_clk, unsigned pixel_chunk); + virtual ~BasePixelPump(); + + void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE; + void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; + + public: // Public API + /** Starting pushing pixels using the supplied display timings. */ + void start(const DisplayTimings &timings); + + /** Immediately stop pushing pixels */ + void stop(); + + /** Get a constant reference of the current display timings */ + const DisplayTimings &timings() const { return _timings; } + + /** Is the pixel pump active and refreshing the display? */ + bool active() const { return evBeginLine.active(); } + + /** Did a buffer underrun occur within this refresh interval? */ + bool underrun() const { return _underrun; } + + /** Is the current line within the visible range? */ + bool visibleLine() const { + return line >= _timings.lineFirstVisible() && + line < _timings.lineFrontPorchStart(); + } + + /** Current pixel position within the visible area */ + unsigned posX() const { return _posX; } + + /** Current pixel position within the visible area */ + unsigned posY() const { + return visibleLine() ? line - _timings.lineFirstVisible() : 0; + } + + /** Output frame buffer */ + FrameBuffer fb; + + protected: // Callbacks + /** + * Get the next pixel from the scan line buffer. + * + * @param p Output pixel value, undefined on underrun + * @return true on success, false on buffer underrun + */ + virtual bool nextPixel(Pixel &p) = 0; + + /** First pixel clock of the first VSync line. */ + virtual void onVSyncBegin() {}; + + /** + * Callback on the first pixel of the line after the end VSync + * region (typically the first pixel of the vertical back porch). + */ + virtual void onVSyncEnd() {}; + + /** + * Start of the HSync region. + * + * @note This is called even for scan lines outside of the visible + * region. + */ + virtual void onHSyncBegin() {}; + + /** + * Start of the first pixel after the HSync region. + * + * @note This is called even for scan lines outside of the visible + * region. + */ + virtual void onHSyncEnd() {}; + + /** + * Buffer underrun occurred on a frame. + * + * This method is called once if there is buffer underrun while + * refreshing the display. The underrun state is reset on the next + * refresh. + * + * @param x Coordinate within the visible region. + * @param y Coordinate within the visible region. + */ + virtual void onUnderrun(unsigned x, unsigned y) {}; + + /** Finished displaying the visible region of a frame */ + virtual void onFrameDone() {}; + + private: // Params + /** Maximum number of pixels to handle per render callback */ + const unsigned pixelChunk; + + private: + /** + * Callback helper class with suspend support. + * + * Unlike a normal EventWrapper, this class suspends an event on + * drain() and restarts it at drainResume(). The suspend operation + * stores the tick relative to curTick() and then deschedules the + * event. The resume operation schedules the event at curTick() + * plus the relative tick stored when the event was suspended. + */ + class PixelEvent : public Event, public Drainable + { + typedef void (BasePixelPump::* CallbackType)(); + + public: + PixelEvent(const char *name, BasePixelPump *parent, CallbackType func); + + DrainState drain() M5_ATTR_OVERRIDE; + void drainResume() M5_ATTR_OVERRIDE; + + void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE; + void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; + + const std::string name() const M5_ATTR_OVERRIDE { return _name; } + void process() M5_ATTR_OVERRIDE { + (parent.*func)(); + } + + bool active() const { return scheduled() || suspended; } + + private: + void suspend(); + void resume(); + + const std::string _name; + BasePixelPump &parent; + const CallbackType func; + + bool suspended; + Tick relativeTick; + }; + + void beginLine(); + void renderPixels(); + + /** Convenience vector when doing operations on all events */ + std::vector<PixelEvent *> pixelEvents; + + PixelEvent evVSyncBegin; + PixelEvent evVSyncEnd; + PixelEvent evHSyncBegin; + PixelEvent evHSyncEnd; + PixelEvent evBeginLine; + PixelEvent evRenderPixels; + + DisplayTimings _timings; + + /** + * Current line (including back porch, front porch, and vsync) + * within a frame. + */ + unsigned line; + /** X-coordinate within the visible region of a frame */ + unsigned _posX; + + /** Did a buffer underrun occur within this refresh interval? */ + bool _underrun; +}; + +#endif // __DEV_PIXELPUMP_HH__ |