pixelpump.cc revision 11012:f2ca575d27fd
1/*
2 * Copyright (c) 2015 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
139
140void
141BasePixelPump::start(const DisplayTimings &timings)
142{
143    _timings = timings;
144
145    // Resize the frame buffer if needed
146    if (_timings.width != fb.width() || _timings.height != fb.height())
147        fb.resize(timings.width, timings.height);
148
149    // Set the current line past the last line in the frame. This
150    // triggers the new frame logic in beginLine().
151    line = _timings.linesPerFrame();
152    schedule(evBeginLine, clockEdge());
153}
154
155void
156BasePixelPump::stop()
157{
158    if (evVSyncEnd.scheduled())
159        deschedule(evVSyncEnd);
160
161    if (evHSyncBegin.scheduled())
162        deschedule(evHSyncBegin);
163
164    if (evHSyncEnd.scheduled())
165        deschedule(evHSyncEnd);
166
167    if (evBeginLine.scheduled())
168        deschedule(evBeginLine);
169
170    if (evRenderPixels.scheduled())
171        deschedule(evRenderPixels);
172}
173
174void
175BasePixelPump::beginLine()
176{
177    _posX = 0;
178    line++;
179    if (line >= _timings.linesPerFrame()) {
180        _underrun = false;
181        line = 0;
182    }
183
184    if (line == _timings.lineVSyncStart()) {
185        onVSyncBegin();
186    } else if (line == _timings.lineVBackPorchStart()) {
187        onVSyncEnd();
188    }
189
190    const Cycles h_sync_begin(0);
191    schedule(evHSyncBegin, clockEdge(h_sync_begin));
192
193    const Cycles h_sync_end(h_sync_begin + _timings.hSync);
194    schedule(evHSyncEnd, clockEdge(h_sync_end));
195
196    // Visible area
197    if (line >= _timings.lineFirstVisible() &&
198        line < _timings.lineFrontPorchStart()) {
199
200        const Cycles h_first_visible(h_sync_end + _timings.hBackPorch);
201        schedule(evRenderPixels, clockEdge(h_first_visible));
202    }
203
204    schedule(evBeginLine, clockEdge(_timings.cyclesPerLine()));
205}
206
207void
208BasePixelPump::renderPixels()
209{
210    // Try to handle multiple pixels at a time; doing so reduces the
211    // accuracy of the underrun detection but lowers simulation
212    // overhead
213    const unsigned x_end(std::min(_posX + pixelChunk, _timings.width));
214    const unsigned pxl_count(x_end - _posX);
215    const unsigned pos_y(posY());
216
217    Pixel pixel(0, 0, 0);
218    const Pixel underrun_pixel(0, 0, 0);
219    for (; _posX < x_end && !_underrun; ++_posX) {
220        if (!nextPixel(pixel)) {
221            warn("Input buffer underrun in BasePixelPump (%u, %u)\n",
222                 _posX, pos_y);
223            _underrun = true;
224            onUnderrun(_posX, pos_y);
225            pixel = underrun_pixel;
226        }
227        fb.pixel(_posX, pos_y) = pixel;
228    }
229
230    // Fill remaining pixels with a dummy pixel value if we ran out of
231    // data
232    for (; _posX < x_end; ++_posX)
233        fb.pixel(_posX, pos_y) = underrun_pixel;
234
235    // Schedule a new event to handle the next block of pixels
236    if (_posX < _timings.width) {
237        schedule(evRenderPixels, clockEdge(Cycles(pxl_count)));
238    } else {
239        if (pos_y == _timings.height - 1)
240            onFrameDone();
241    }
242}
243
244BasePixelPump::PixelEvent::PixelEvent(
245    const char *name, BasePixelPump *_parent, CallbackType _func)
246    : Event(), Drainable(),
247      _name(name), parent(*_parent), func(_func),
248      suspended(false),
249      relativeTick(0)
250{
251    parent.pixelEvents.push_back(this);
252}
253
254DrainState
255BasePixelPump::PixelEvent::drain()
256{
257    if (scheduled())
258        suspend();
259    return DrainState::Drained;
260}
261
262void
263BasePixelPump::PixelEvent::drainResume()
264{
265    if (suspended)
266        resume();
267}
268
269void
270BasePixelPump::PixelEvent::serialize(CheckpointOut &cp) const
271{
272    assert(!scheduled());
273    Event::serialize(cp);
274    SERIALIZE_SCALAR(suspended);
275    SERIALIZE_SCALAR(relativeTick);
276}
277
278void
279BasePixelPump::PixelEvent::unserialize(CheckpointIn &cp)
280{
281    Event::unserialize(cp);
282    UNSERIALIZE_SCALAR(suspended);
283    UNSERIALIZE_SCALAR(relativeTick);
284    assert(!scheduled());
285}
286
287void
288BasePixelPump::PixelEvent::suspend()
289{
290    assert(scheduled());
291    assert(!suspended);
292
293    suspended = true;
294    relativeTick = when() - curTick();
295    parent.deschedule(this);
296}
297
298void
299BasePixelPump::PixelEvent::resume()
300{
301    assert(!scheduled());
302    assert(suspended);
303    parent.schedule(this, relativeTick + curTick());
304    suspended = false;
305    relativeTick = 0;
306}
307