--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__