diff options
author | Chris Emmons <Chris.Emmons@arm.com> | 2013-04-22 13:20:31 -0400 |
---|---|---|
committer | Chris Emmons <Chris.Emmons@arm.com> | 2013-04-22 13:20:31 -0400 |
commit | 121b15a54da77ef77e98ff59621e1c5b0f1f1f52 (patch) | |
tree | d9e9993c551ecc94ee1f9f88c5ab02f87760a328 | |
parent | aa08069b3fb9a564df755ec558fd64ba076b0ef3 (diff) | |
download | gem5-121b15a54da77ef77e98ff59621e1c5b0f1f1f52.tar.xz |
ARM: Add support for HDLCD controller for TC2 and newer Versatile Express tiles.
Newer core tiles / daughterboards for the Versatile Express platform have an
HDLCD controller that supports HD-quality output. This patch adds an
implementation of the controller.
-rw-r--r-- | src/dev/arm/RealView.py | 14 | ||||
-rw-r--r-- | src/dev/arm/SConscript | 2 | ||||
-rw-r--r-- | src/dev/arm/hdlcd.cc | 856 | ||||
-rw-r--r-- | src/dev/arm/hdlcd.hh | 502 |
4 files changed, 1374 insertions, 0 deletions
diff --git a/src/dev/arm/RealView.py b/src/dev/arm/RealView.py index f01572e40..b5d41ce93 100644 --- a/src/dev/arm/RealView.py +++ b/src/dev/arm/RealView.py @@ -142,6 +142,16 @@ class Pl111(AmbaDmaDevice): vnc = Param.VncInput(Parent.any, "Vnc server for remote frame buffer display") amba_id = 0x00141111 +class HDLcd(AmbaDmaDevice): + type = 'HDLcd' + cxx_header = "dev/arm/hdlcd.hh" + pixel_clock = Param.Clock('65MHz', "Clock frequency of the pixel clock " + "(i.e. PXLREFCLK / OSCCLK 5; 23.75MHz " + "default up to 165MHz)") + vnc = Param.VncInput(Parent.any, "Vnc server for remote frame buffer " + "display") + amba_id = 0x00141000 + class RealView(Platform): type = 'RealView' cxx_header = "dev/arm/realview.hh" @@ -333,6 +343,7 @@ class VExpress_EMM(RealView): timer0 = Sp804(int_num0=34, int_num1=34, pio_addr=0x1C110000, clock0='1MHz', clock1='1MHz') timer1 = Sp804(int_num0=35, int_num1=35, pio_addr=0x1C120000, clock0='1MHz', clock1='1MHz') clcd = Pl111(pio_addr=0x1c1f0000, int_num=46) + hdlcd = HDLcd(pio_addr=0x2b000000, int_num=117) kmi0 = Pl050(pio_addr=0x1c060000, int_num=44) kmi1 = Pl050(pio_addr=0x1c070000, int_num=45, is_mouse=True) cf_ctrl = IdeController(disks=[], pci_func=0, pci_dev=0, pci_bus=2, @@ -376,9 +387,11 @@ class VExpress_EMM(RealView): def attachOnChipIO(self, bus, bridge): self.gic.pio = bus.master self.local_cpu_timer.pio = bus.master + self.hdlcd.dma = bus.slave # Bridge ranges based on excluding what is part of on-chip I/O # (gic, a9scu) bridge.ranges = [AddrRange(0x2F000000, size='16MB'), + AddrRange(0x2B000000, size='4MB'), AddrRange(0x30000000, size='256MB'), AddrRange(0x40000000, size='512MB'), AddrRange(0x18000000, size='64MB'), @@ -394,6 +407,7 @@ class VExpress_EMM(RealView): self.timer1.pio = bus.master self.clcd.pio = bus.master self.clcd.dma = bus.slave + self.hdlcd.pio = bus.master self.kmi0.pio = bus.master self.kmi1.pio = bus.master self.cf_ctrl.pio = bus.master diff --git a/src/dev/arm/SConscript b/src/dev/arm/SConscript index 048fe2444..60a85220e 100644 --- a/src/dev/arm/SConscript +++ b/src/dev/arm/SConscript @@ -50,6 +50,7 @@ if env['TARGET_ISA'] == 'arm': Source('gic_pl390.cc') Source('pl011.cc') Source('pl111.cc') + Source('hdlcd.cc') Source('kmi.cc') Source('timer_sp804.cc') Source('rv_ctrl.cc') @@ -58,6 +59,7 @@ if env['TARGET_ISA'] == 'arm': Source('timer_cpulocal.cc') DebugFlag('AMBA') + DebugFlag('HDLcd') DebugFlag('PL111') DebugFlag('Pl050') DebugFlag('GIC') diff --git a/src/dev/arm/hdlcd.cc b/src/dev/arm/hdlcd.cc new file mode 100644 index 000000000..14128c730 --- /dev/null +++ b/src/dev/arm/hdlcd.cc @@ -0,0 +1,856 @@ +/* + * Copyright (c) 2010-2013 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: Chris Emmons + */ + +#include "base/vnc/vncinput.hh" +#include "base/bitmap.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "debug/HDLcd.hh" +#include "debug/Uart.hh" +#include "dev/arm/amba_device.hh" +#include "dev/arm/base_gic.hh" +#include "dev/arm/hdlcd.hh" +#include "mem/packet.hh" +#include "mem/packet_access.hh" +#include "sim/system.hh" + +using std::vector; + + +// initialize hdlcd registers +HDLcd::HDLcd(const Params *p) + : AmbaDmaDevice(p), version(VERSION_RESETV), + int_rawstat(0), int_clear(0), int_mask(0), int_status(0), + fb_base(0), fb_line_length(0), fb_line_count(0), fb_line_pitch(0), + bus_options(BUS_OPTIONS_RESETV), + v_sync(0), v_back_porch(0), v_data(0), v_front_porch(0), + h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0), + polarities(0), command(0), pixel_format(0), + red_select(0), green_select(0), blue_select(0), + pixelClock(p->pixel_clock), vnc(p->vnc), bmp(NULL), pic(NULL), + frameReadStartTime(0), + dmaStartAddr(0), dmaCurAddr(0), dmaMaxAddr(0), dmaPendingNum(0), + frameUnderrun(false), virtualDisplayBuffer(NULL), pixelBufferSize(0), + pixelIndex(0), doUpdateParams(false), frameUnderway(false), + dmaBytesInFlight(0), + startFrameEvent(this), endFrameEvent(this), renderPixelEvent(this), + fillPixelBufferEvent(this), intEvent(this), + dmaDoneEventAll(MAX_OUTSTANDING_DMA_REQ_CAPACITY, this), + dmaDoneEventFree(MAX_OUTSTANDING_DMA_REQ_CAPACITY) +{ + pioSize = 0xFFFF; + + for (int i = 0; i < MAX_OUTSTANDING_DMA_REQ_CAPACITY; ++i) + dmaDoneEventFree[i] = &dmaDoneEventAll[i]; + + if (vnc) + vnc->setFramebufferAddr(NULL); +} + +HDLcd::~HDLcd() +{ + if (virtualDisplayBuffer) + delete [] virtualDisplayBuffer; +} + +// read registers and frame buffer +Tick +HDLcd::read(PacketPtr pkt) +{ + uint32_t data = 0; + const Addr daddr = pkt->getAddr() - pioAddr; + + DPRINTF(HDLcd, "read register BASE+0x%04x size=%d\n", daddr, + pkt->getSize()); + + assert(pkt->getAddr() >= pioAddr && + pkt->getAddr() < pioAddr + pioSize && + pkt->getSize() == 4); + + pkt->allocate(); + + switch (daddr) { + case Version: + data = version; + break; + case Int_RawStat: + data = int_rawstat; + break; + case Int_Clear: + panic("HDLCD INT_CLEAR register is Write-Only\n"); + break; + case Int_Mask: + data = int_mask; + break; + case Int_Status: + data = int_status; + break; + case Fb_Base: + data = fb_base; + break; + case Fb_Line_Length: + data = fb_line_length; + break; + case Fb_Line_Count: + data = fb_line_count; + break; + case Fb_Line_Pitch: + data = fb_line_pitch; + break; + case Bus_Options: + data = bus_options; + break; + case V_Sync: + data = v_sync; + break; + case V_Back_Porch: + data = v_back_porch; + break; + case V_Data: + data = v_data; + break; + case V_Front_Porch: + data = v_front_porch; + break; + case H_Sync: + data = h_sync; + break; + case H_Back_Porch: + data = h_back_porch; + break; + case H_Data: + data = h_data; + break; + case H_Front_Porch: + data = h_front_porch; + break; + case Polarities: + data = polarities; + break; + case Command: + data = command; + break; + case Pixel_Format: + data = pixel_format; + break; + case Red_Select: + data = red_select; + break; + case Green_Select: + data = green_select; + break; + case Blue_Select: + data = blue_select; + break; + default: + panic("Tried to read HDLCD register that doesn't exist\n", daddr); + break; + } + + pkt->set<uint32_t>(data); + pkt->makeAtomicResponse(); + return pioDelay; +} + +// write registers and frame buffer +Tick +HDLcd::write(PacketPtr pkt) +{ + assert(pkt->getAddr() >= pioAddr && + pkt->getAddr() < pioAddr + pioSize && + pkt->getSize() == 4); + + const uint32_t data = pkt->get<uint32_t>(); + const Addr daddr = pkt->getAddr() - pioAddr; + + DPRINTF(HDLcd, "write register BASE+%0x04x <= 0x%08x\n", daddr, + pkt->get<uint32_t>()); + + switch (daddr) { + case Version: + panic("HDLCD VERSION register is read-Only\n"); + break; + case Int_RawStat: + int_rawstat = data; + break; + case Int_Clear: + int_clear = data; + break; + case Int_Mask: + int_mask = data; + break; + case Int_Status: + panic("HDLCD INT_STATUS register is read-Only\n"); + break; + case Fb_Base: + fb_base = data; + DPRINTF(HDLcd, "HDLCD Frame Buffer located at addr 0x%08x\n", fb_base); + break; + case Fb_Line_Length: + fb_line_length = data; + DPRINTF(HDLcd, "HDLCD res = %d x %d\n", width(), height()); + break; + case Fb_Line_Count: + fb_line_count = data; + DPRINTF(HDLcd, "HDLCD res = %d x %d\n", width(), height()); + break; + case Fb_Line_Pitch: + fb_line_pitch = data; + break; + case Bus_Options: { + BusOptsReg old_bus_options; + old_bus_options = bus_options; + bus_options = data; + if (bus_options.max_outstanding != old_bus_options.max_outstanding) + DPRINTF(HDLcd, + "Changing HDLcd outstanding dma transactions from %d to %d\n", + old_bus_options.max_outstanding, bus_options.max_outstanding); + if (bus_options.burst_len != old_bus_options.burst_len) + DPRINTF(HDLcd, + "Changing HDLcd dma burst length from %d bytes to %d bytes\n", + old_bus_options.burst_len, bus_options.burst_len); } + break; + case V_Sync: + v_sync = data; + break; + case V_Back_Porch: + v_back_porch = data; + break; + case V_Data: + v_data = data; + break; + case V_Front_Porch: + v_front_porch = data; + break; + case H_Sync: + h_sync = data; + break; + case H_Back_Porch: + h_back_porch = data; + break; + case H_Data: + h_data = data; + break; + case H_Front_Porch: + h_front_porch = data; + break; + case Polarities: + polarities = data; + break; + case Command: { + CommandReg new_command; + new_command = data; + if (new_command.enable != command.enable) { + DPRINTF(HDLcd, "HDLCD switched %s\n", + new_command.enable==0 ? "off" : "on"); + if (new_command.enable) { + doUpdateParams = true; + if (!frameUnderway) { + schedule(startFrameEvent, nextCycle()); + } + } + } + command = new_command; } + break; + case Pixel_Format: + pixel_format = data; + DPRINTF(HDLcd, "HDLCD res = %d x %d\n", width(), height()); + DPRINTF(HDLcd, "HDLCD bytes per pixel = %d\n", bytesPerPixel()); + DPRINTF(HDLcd, "HDLCD endianness = %s\n", + pixel_format.big_endian ? "big" : "little"); + break; + case Red_Select: + red_select = data; + break; + case Green_Select: + green_select = data; + break; + case Blue_Select: + blue_select = data; + break; + default: + panic("Tried to write HDLCD register that doesn't exist\n", daddr); + break; + } + + pkt->makeAtomicResponse(); + return pioDelay; +} + +void +HDLcd::updateVideoParams(bool unserializing = false) +{ + const uint16_t bpp = bytesPerPixel() << 3; + const size_t buffer_size = bytesPerPixel() * width() * height(); + + // updating these parameters while LCD is enabled is not supported + if (frameUnderway && !unserializing) + panic("Attempting to change some HDLCD parameters while the controller" + " is active is not allowed"); + + // resize the virtualDisplayBuffer unless we are unserializing - it may + // have changed size + // there must be no outstanding DMA transactions for this to work + if (!unserializing) { + assert(dmaPendingNum == 0); + if (virtualDisplayBuffer) + delete [] virtualDisplayBuffer; + virtualDisplayBuffer = new uint8_t[buffer_size]; + memset(virtualDisplayBuffer, 0, buffer_size); + } + + assert(virtualDisplayBuffer); + if (vnc) + vnc->setFramebufferAddr(virtualDisplayBuffer); + + if (bmp) + delete bmp; + + DPRINTF(HDLcd, "bpp = %d\n", bpp); + DPRINTF(HDLcd, "display size = %d x %d\n", width(), height()); +#if TRACING_ON + const size_t totalLinesPerFrame = v_back_porch.val + 1 + + v_data.val + 1 + + v_front_porch.val + 1 + + v_sync.val + 1; + const double fps = (double)SimClock::Frequency / + (double)(PClksPerLine() * totalLinesPerFrame * pixelClock); +#endif + DPRINTF(HDLcd, "simulated refresh rate ~ %.1ffps generating ~ %.1fMB/s " + "traffic ([%.1fMHz, T=%d sim clocks] pclk, %d bpp => %.1fMB/s peak requirement)\n", + fps, + fps * buffer_size / 1024 / 1024, + (double)SimClock::Frequency / pixelClock / 1000000.0, + pixelClock, + bpp, + (double)(SimClock::Frequency / pixelClock * (bpp / 8)) / 1024 / 1024); + + if (pixel_format.big_endian) + panic("Big Endian pixel format not implemented by HDLcd controller"); + + if (vnc) { + if ((bpp == 24) && + (red_select.size == 8) && + (blue_select.size == 8) && + (green_select.size == 8) && + (green_select.offset == 8)) { + if ((blue_select.offset == 0) && + (red_select.offset == 16)) { + vnc->setFrameBufferParams(VideoConvert::rgb8888, width(), + height()); + bmp = new Bitmap(VideoConvert::rgb8888, width(), height(), + virtualDisplayBuffer); + DPRINTF(HDLcd, "color mode: rgb888\n"); + } else if ((red_select.offset == 0) && + (blue_select.offset == 16)) { + vnc->setFrameBufferParams(VideoConvert::bgr8888, width(), + height()); + bmp = new Bitmap(VideoConvert::bgr8888, width(), height(), + virtualDisplayBuffer); + DPRINTF(HDLcd, "color mode: bgr888\n"); + } + } else if ((bpp == 16) && + (red_select.size == 5) && + (blue_select.size == 5) && + (green_select.size == 6) && + (green_select.offset == 5)) { + if ((blue_select.offset == 0) && + (red_select.offset == 11)) { + vnc->setFrameBufferParams(VideoConvert::rgb565, width(), + height()); + bmp = new Bitmap(VideoConvert::rgb565, width(), height(), + virtualDisplayBuffer); + DPRINTF(HDLcd, "color mode: rgb565\n"); + } else if ((red_select.offset == 0) && + (blue_select.offset == 11)) { + vnc->setFrameBufferParams(VideoConvert::bgr565, width(), + height()); + bmp = new Bitmap(VideoConvert::bgr565, width(), height(), + virtualDisplayBuffer); + DPRINTF(HDLcd, "color mode: bgr565\n"); + } + } else { + DPRINTF(HDLcd, "color mode: undefined\n"); + panic("Unimplemented video mode\n"); + } + } +} + +void +HDLcd::startFrame() +{ + // 0. Check that we are in the appropriate state + assert(!frameUnderway); + if (!command.enable) + return; + DPRINTF(HDLcd, "Frame read started\n"); + if (doUpdateParams) { + updateVideoParams(); + doUpdateParams = false; + } + frameUnderway = true; + assert(virtualDisplayBuffer); + assert(pixelBufferSize == 0); + assert(dmaBytesInFlight == 0); + assert(dmaPendingNum == 0); + assert(dmaDoneEventFree.size() == dmaDoneEventAll.size()); + assert(!renderPixelEvent.scheduled()); + // currently only support positive line pitches equal to the line length + assert(width() * bytesPerPixel() == fb_line_pitch); + + // 1. Start DMA'ing the frame; subsequent transactions created as we go + dmaCurAddr = dmaStartAddr = fb_base; + dmaMaxAddr = static_cast<Addr>(width() * height() * bytesPerPixel()) + + dmaCurAddr; + frameReadStartTime = curTick(); + pixelIndex = 0; + frameUnderrun = false; + fillPixelBuffer(); + + // 2. Schedule first pixelclock read; subsequent reads generated as we go + Tick firstPixelReadTick = curTick() + pixelClock * ( + PClksPerLine() * (v_sync.val + 1 + + v_back_porch.val + 1) + + h_sync.val + 1 + + h_back_porch.val + 1); + schedule(renderPixelEvent, firstPixelReadTick); +} + +void +HDLcd::fillPixelBuffer() +{ + // - am I under the LCD dma transaction total? + // - do I have more data to transfer? + // - have I not yet underrun for this frame? + // - is there room to put the data in the pixel buffer including any + // outstanding dma transfers in flight? + while ((dmaPendingNum < maxOutstandingDma()) && + (dmaMaxAddr > dmaCurAddr) && + !frameUnderrun && + bytesFreeInPixelBuffer() > dmaBurstLength() * AXI_PORT_WIDTH) { + // try largest transaction size allowed first but switch to smaller + // sizes for trailing bytes + size_t transaction_size = dmaBurstLength() * AXI_PORT_WIDTH; + while (transaction_size > (dmaMaxAddr - dmaCurAddr)) + transaction_size >>= 1; + assert(transaction_size > 0); + + // concurrent dma reads need different dma done events + // due to assertion in scheduling state + ++dmaPendingNum; + + assert(!dmaDoneEventFree.empty()); + DmaDoneEvent *event(dmaDoneEventFree.back()); + dmaDoneEventFree.pop_back(); + assert(event); + assert(!event->scheduled()); + + // We use a uncachable request here because the requests from the CPU + // will be uncacheable as well. If we have uncacheable and cacheable + // requests in the memory system for the same address it won't be + // pleased + event->setTransactionSize(transaction_size); + dmaPort.dmaAction(MemCmd::ReadReq, dmaCurAddr, transaction_size, event, + virtualDisplayBuffer + dmaCurAddr - dmaStartAddr, + 0, Request::UNCACHEABLE); + dmaCurAddr += transaction_size; + dmaBytesInFlight += transaction_size; + } +} + +void +HDLcd::renderPixel() +{ + // try to handle multiple pixels at a time; doing so reduces the accuracy + // of the underrun detection but lowers simulation overhead + const size_t count = 32; + assert(width() % count == 0); // not set up to handle trailing pixels + + // have we underrun on this frame anytime before? + if (frameUnderrun) { + // the LCD controller gives up on a frame if an underrun occurs and + // resumes regular operation on the next frame + pixelBufferSize = 0; + } else { + // did we underrun on this set of pixels? + if (pixelBufferSize < bytesPerPixel() * count) { + warn("HDLcd controller buffer underrun\n"); + frameUnderrun = true; + int_rawstat.underrun = 1; + if (!intEvent.scheduled()) + schedule(intEvent, nextCycle()); + } else { + // emulate the pixel read from the internal buffer + pixelBufferSize -= bytesPerPixel() * count; + } + } + + // the DMA may have previously stalled due to the buffer being full; + // give it a kick; it knows not to fill if at end of frame, underrun, etc + if (!fillPixelBufferEvent.scheduled()) + schedule(fillPixelBufferEvent, nextCycle()); + + // schedule the next pixel read according to where it is in the frame + pixelIndex += count; + assert(pixelIndex <= width() * height()); + size_t x = pixelIndex % width(); + Tick nextEventTick = curTick(); + if (x == 0) { + // start of new line + nextEventTick += pixelClock * ((h_front_porch.val + 1) + + (h_back_porch.val + 1) + + (h_sync.val + 1)); + if (pixelIndex == width() * height()) { + // end of frame + nextEventTick += PClksPerLine() * (v_front_porch.val + 1) * + pixelClock; + schedule(endFrameEvent, nextEventTick); + return; + } + } else { + nextEventTick += pixelClock * count; + } + + schedule(renderPixelEvent, nextEventTick); +} + +void +HDLcd::endFrame() { + assert(pixelBufferSize == 0); + assert(dmaPendingNum == 0); + assert(dmaBytesInFlight == 0); + assert(dmaDoneEventFree.size() == dmaDoneEventAll.size()); + + if (vnc) + vnc->setDirty(); + + if (!pic) + pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()), true); + + assert(bmp); + assert(pic); + pic->seekp(0); + bmp->write(pic); + + // start the next frame + frameUnderway = false; + startFrame(); +} + +void +HDLcd::dmaDone(DmaDoneEvent *event) +{ + const size_t transactionLength = event->getTransactionSize(); + assert(pixelBufferSize + transactionLength < PIXEL_BUFFER_CAPACITY); + assert(dmaCurAddr <= dmaMaxAddr); + + dmaDoneEventFree.push_back(event); + --dmaPendingNum; + assert(MAX_OUTSTANDING_DMA_REQ_CAPACITY - dmaDoneEventFree.size() == + dmaPendingNum); + + // add the data to the pixel buffer + dmaBytesInFlight -= transactionLength; + pixelBufferSize += transactionLength; + + // schedule another dma transaction if: + // - we're not done reading the frame + // - there is sufficient room in the pixel buffer for another transaction + // - another fillPixelBufferEvent is not already scheduled + const size_t targetTransSize = dmaBurstLength() * AXI_PORT_WIDTH; + if ((dmaCurAddr < dmaMaxAddr) && + (bytesFreeInPixelBuffer() + targetTransSize < PIXEL_BUFFER_CAPACITY) && + !fillPixelBufferEvent.scheduled()) { + schedule(fillPixelBufferEvent, nextCycle()); + } +} + +void +HDLcd::serialize(std::ostream &os) +{ + DPRINTF(HDLcd, "Serializing ARM HDLCD\n"); + + const uint32_t version_serial = version; + SERIALIZE_SCALAR(version_serial); + const uint32_t int_rawstat_serial = int_rawstat; + SERIALIZE_SCALAR(int_rawstat_serial); + const uint32_t int_clear_serial = int_clear; + SERIALIZE_SCALAR(int_clear_serial); + const uint32_t int_mask_serial = int_mask; + SERIALIZE_SCALAR(int_mask_serial); + const uint32_t int_status_serial = int_status; + SERIALIZE_SCALAR(int_status_serial); + + SERIALIZE_SCALAR(fb_base); + SERIALIZE_SCALAR(fb_line_length); + + const uint32_t fb_line_count_serial = fb_line_count; + SERIALIZE_SCALAR(fb_line_count_serial); + + SERIALIZE_SCALAR(fb_line_pitch); + + const uint32_t bus_options_serial = bus_options; + SERIALIZE_SCALAR(bus_options_serial); + const uint32_t v_sync_serial = v_sync; + SERIALIZE_SCALAR(v_sync_serial); + const uint32_t v_back_porch_serial = v_back_porch; + SERIALIZE_SCALAR(v_back_porch_serial); + const uint32_t v_data_serial = v_data; + SERIALIZE_SCALAR(v_data_serial); + const uint32_t v_front_porch_serial = v_front_porch; + SERIALIZE_SCALAR(v_front_porch_serial); + const uint32_t h_sync_serial = h_sync; + SERIALIZE_SCALAR(h_sync_serial); + const uint32_t h_back_porch_serial = h_back_porch; + SERIALIZE_SCALAR(h_back_porch_serial); + const uint32_t h_data_serial = h_data; + SERIALIZE_SCALAR(h_data_serial); + const uint32_t h_front_porch_serial = h_front_porch; + SERIALIZE_SCALAR(h_front_porch_serial); + const uint32_t polarities_serial = polarities; + SERIALIZE_SCALAR(polarities_serial); + const uint32_t command_serial = command; + SERIALIZE_SCALAR(command_serial); + const uint32_t pixel_format_serial = pixel_format; + SERIALIZE_SCALAR(pixel_format_serial); + const uint32_t red_select_serial = red_select; + SERIALIZE_SCALAR(red_select_serial); + const uint32_t green_select_serial = green_select; + SERIALIZE_SCALAR(green_select_serial); + const uint32_t blue_select_serial = blue_select; + SERIALIZE_SCALAR(blue_select_serial); + + SERIALIZE_SCALAR(frameReadStartTime); + SERIALIZE_SCALAR(dmaStartAddr); + SERIALIZE_SCALAR(dmaCurAddr); + SERIALIZE_SCALAR(dmaMaxAddr); + SERIALIZE_SCALAR(dmaPendingNum); + SERIALIZE_SCALAR(frameUnderrun); + + const size_t buffer_size = bytesPerPixel() * width() * height(); + SERIALIZE_ARRAY(virtualDisplayBuffer, buffer_size); + + SERIALIZE_SCALAR(pixelBufferSize); + SERIALIZE_SCALAR(pixelIndex); + SERIALIZE_SCALAR(doUpdateParams); + SERIALIZE_SCALAR(frameUnderway); + SERIALIZE_SCALAR(dmaBytesInFlight); + + Tick start_event_time = 0; + Tick end_event_time = 0; + Tick render_pixel_event_time = 0; + Tick fill_pixel_buffer_event_time = 0; + Tick int_event_time = 0; + if (startFrameEvent.scheduled()) + start_event_time = startFrameEvent.when(); + if (endFrameEvent.scheduled()) + end_event_time = endFrameEvent.when(); + if (renderPixelEvent.scheduled()) + render_pixel_event_time = renderPixelEvent.when(); + if (fillPixelBufferEvent.scheduled()) + fill_pixel_buffer_event_time = fillPixelBufferEvent.when(); + if (intEvent.scheduled()) + int_event_time = intEvent.when(); + SERIALIZE_SCALAR(start_event_time); + SERIALIZE_SCALAR(end_event_time); + SERIALIZE_SCALAR(render_pixel_event_time); + SERIALIZE_SCALAR(fill_pixel_buffer_event_time); + SERIALIZE_SCALAR(int_event_time); + + vector<Tick> dma_done_event_tick(MAX_OUTSTANDING_DMA_REQ_CAPACITY); + vector<size_t> dma_done_event_burst_len(MAX_OUTSTANDING_DMA_REQ_CAPACITY); + for (int x = 0; x < MAX_OUTSTANDING_DMA_REQ_CAPACITY; ++x) { + dma_done_event_tick[x] = dmaDoneEventAll[x].scheduled() ? + dmaDoneEventAll[x].when() : 0; + dma_done_event_burst_len[x] = dmaDoneEventAll[x].scheduled() ? + dmaDoneEventAll[x].getTransactionSize() : 0; + } + arrayParamOut(os, "dma_done_event_tick", dma_done_event_tick); + arrayParamOut(os, "dma_done_event_burst_length", dma_done_event_burst_len); +} + +void +HDLcd::unserialize(Checkpoint *cp, const std::string §ion) +{ + uint32_t version_serial, int_rawstat_serial, int_clear_serial, + int_mask_serial, int_status_serial, fb_line_count_serial, + bus_options_serial, v_sync_serial, v_back_porch_serial, + v_data_serial, v_front_porch_serial, h_sync_serial, + h_back_porch_serial, h_data_serial, h_front_porch_serial, + polarities_serial, command_serial, pixel_format_serial, + red_select_serial, green_select_serial, blue_select_serial; + + DPRINTF(HDLcd, "Unserializing ARM HDLCD\n"); + + UNSERIALIZE_SCALAR(version_serial); + version = version_serial; + UNSERIALIZE_SCALAR(int_rawstat_serial); + int_rawstat = int_rawstat_serial; + UNSERIALIZE_SCALAR(int_clear_serial); + int_clear = int_clear_serial; + UNSERIALIZE_SCALAR(int_mask_serial); + int_mask = int_mask_serial; + UNSERIALIZE_SCALAR(int_status_serial); + int_status = int_status_serial; + + UNSERIALIZE_SCALAR(fb_base); + UNSERIALIZE_SCALAR(fb_line_length); + + UNSERIALIZE_SCALAR(fb_line_count_serial); + fb_line_count = fb_line_count_serial; + + UNSERIALIZE_SCALAR(fb_line_pitch); + + UNSERIALIZE_SCALAR(bus_options_serial); + bus_options = bus_options_serial; + UNSERIALIZE_SCALAR(v_sync_serial); + v_sync = v_sync_serial; + UNSERIALIZE_SCALAR(v_back_porch_serial); + v_back_porch = v_back_porch_serial; + UNSERIALIZE_SCALAR(v_data_serial); + v_data = v_data_serial; + UNSERIALIZE_SCALAR(v_front_porch_serial); + v_front_porch = v_front_porch_serial; + UNSERIALIZE_SCALAR(h_sync_serial); + h_sync = h_sync_serial; + UNSERIALIZE_SCALAR(h_back_porch_serial); + h_back_porch = h_back_porch_serial; + UNSERIALIZE_SCALAR(h_data_serial); + h_data = h_data_serial; + UNSERIALIZE_SCALAR(h_front_porch_serial); + h_front_porch = h_front_porch_serial; + UNSERIALIZE_SCALAR(polarities_serial); + polarities = polarities_serial; + UNSERIALIZE_SCALAR(command_serial); + command = command_serial; + UNSERIALIZE_SCALAR(pixel_format_serial); + pixel_format = pixel_format_serial; + UNSERIALIZE_SCALAR(red_select_serial); + red_select = red_select_serial; + UNSERIALIZE_SCALAR(green_select_serial); + green_select = green_select_serial; + UNSERIALIZE_SCALAR(blue_select_serial); + blue_select = blue_select_serial; + + UNSERIALIZE_SCALAR(frameReadStartTime); + UNSERIALIZE_SCALAR(dmaStartAddr); + UNSERIALIZE_SCALAR(dmaCurAddr); + UNSERIALIZE_SCALAR(dmaMaxAddr); + UNSERIALIZE_SCALAR(dmaPendingNum); + UNSERIALIZE_SCALAR(frameUnderrun); + UNSERIALIZE_SCALAR(dmaBytesInFlight); + + const size_t buffer_size = bytesPerPixel() * width() * height(); + virtualDisplayBuffer = new uint8_t[buffer_size]; + UNSERIALIZE_ARRAY(virtualDisplayBuffer, buffer_size); + + UNSERIALIZE_SCALAR(pixelBufferSize); + UNSERIALIZE_SCALAR(pixelIndex); + UNSERIALIZE_SCALAR(doUpdateParams); + UNSERIALIZE_SCALAR(frameUnderway); + + Tick start_event_time = 0; + Tick end_event_time = 0; + Tick render_pixel_event_time = 0; + Tick fill_pixel_buffer_event_time = 0; + Tick int_event_time = 0; + UNSERIALIZE_SCALAR(start_event_time); + UNSERIALIZE_SCALAR(end_event_time); + UNSERIALIZE_SCALAR(render_pixel_event_time); + UNSERIALIZE_SCALAR(fill_pixel_buffer_event_time); + UNSERIALIZE_SCALAR(int_event_time); + if (start_event_time) + schedule(startFrameEvent, start_event_time); + if (end_event_time) + schedule(endFrameEvent, end_event_time); + if (render_pixel_event_time) + schedule(renderPixelEvent, render_pixel_event_time); + if (fill_pixel_buffer_event_time) + schedule(fillPixelBufferEvent, fill_pixel_buffer_event_time); + if (int_event_time) + schedule(intEvent, int_event_time); + + vector<Tick> dma_done_event_tick(MAX_OUTSTANDING_DMA_REQ_CAPACITY); + vector<Tick> dma_done_event_burst_len(MAX_OUTSTANDING_DMA_REQ_CAPACITY); + arrayParamIn(cp, section, "dma_done_event_tick", dma_done_event_tick); + arrayParamIn(cp, section, "dma_done_event_burst_length", dma_done_event_burst_len); + dmaDoneEventFree.clear(); + for (int x = 0; x < MAX_OUTSTANDING_DMA_REQ_CAPACITY; ++x) { + if (dma_done_event_tick[x]) { + dmaDoneEventAll[x].setTransactionSize(dma_done_event_burst_len[x]); + schedule(dmaDoneEventAll[x], dma_done_event_tick[x]); + } else + dmaDoneEventFree.push_back(&dmaDoneEventAll[x]); + } + assert(MAX_OUTSTANDING_DMA_REQ_CAPACITY - dmaDoneEventFree.size() == dmaPendingNum); + + if (frameUnderway) { + updateVideoParams(true); + if (vnc) + vnc->setDirty(); + } +} + +void +HDLcd::generateInterrupt() +{ + int_status = int_rawstat & int_mask; + DPRINTF(HDLcd, "Generate Interrupt: int_rawstat=0x%08x int_mask=0x%08x " + "int_status=0x%08x\n", + (uint32_t)int_rawstat, (uint32_t)int_mask, (uint32_t)int_status); + + if (int_status != 0) { + gic->sendInt(intNum); + DPRINTF(HDLcd, " -- Generated\n"); + } +} + +AddrRangeList +HDLcd::getAddrRanges() const +{ + AddrRangeList ranges; + ranges.push_back(RangeSize(pioAddr, pioSize)); + return ranges; +} + +HDLcd * +HDLcdParams::create() +{ + return new HDLcd(this); +} diff --git a/src/dev/arm/hdlcd.hh b/src/dev/arm/hdlcd.hh new file mode 100644 index 000000000..ab4df5ed6 --- /dev/null +++ b/src/dev/arm/hdlcd.hh @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2010-2013 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: Chris Emmons + */ + + +/** @file + * Implementiation of the ARM HDLcd controller. + * + * This implementation aims to have sufficient detail such that underrun + * conditions are reasonable / behave similar to reality. There are two + * 'engines' going at once. First, the DMA engine running at LCD clock + * frequency is responsible for filling the controller's internal buffer. + * The second engine runs at the pixel clock frequency and reads the pixels + * out of the internal buffer. The pixel rendering engine uses front / back + * porch and sync delays between lines and frames. + * + * If the pixel rendering engine does not have a pixel to display, it will + * cause an underrun event. The HDLcd controller, per spec, will stop + * issuing DMA requests for the rest of the frame and resume normal behavior + * on the subsequent frame. What pixels are rendered upon an underrun + * condition is different than the real hardware; while the user will see + * artifacts (previous frame mixed with current frame), it is not the same + * behavior as real hardware which repeats the last pixel value for the rest + * of the current frame. This compromise was made to save on memory and + * complexity and assumes that it is not important to accurately model the + * content of an underrun frame. + * + * KNOWN ISSUES + * 1. The default kernel driver used in testing sets the line count to one + * less than the expected 768. However, it also sets the v_count to 767. + * The controller specifies that 1 should be added to v_count but does not + * specify adding 1 to the line count. The driver is probably wrong. + * However, to sync these two numbers up, this model uses fb_line_count and + * fb_line_length rather than using v_data or h_data values to determine the + * width and height of the frame; those values are ignored. + * 2. The HDLcd is implemented here as an AmbaDmaDevice, but it doesn't have + * an AMBA ID as far as I know. That is the only bit of the AmbaDmaDevice + * interface that is irrelevant to it, so a fake AMBA ID is used for now. + * I didn't think inserting an extra layer of hierachy between AmbaDmaDevice + * and DmaDevice would be helpful to anyone else, but that may be the right + * answer. + * 3. The internal buffer size is either 1 or 2 KB depending on which + * specification is referenced for the different Versatile Express tiles. + * This implementation uses the larger 2 KB buffer by default. + */ + +#ifndef __DEV_ARM_HDLCD_HH__ +#define __DEV_ARM_HDLCD_HH__ + +#include <fstream> + +#include "dev/arm/amba_device.hh" +#include "params/HDLcd.hh" +#include "sim/serialize.hh" + +class VncInput; +class Bitmap; + +class HDLcd: public AmbaDmaDevice +{ + protected: + /** fake AMBA ID -- unused */ + static const uint64_t AMBA_ID = ULL(0xb105f00d00141000); + + /** ARM HDLcd register offsets */ + enum RegisterOffset { + Version = 0x0000, + Int_RawStat = 0x0010, + Int_Clear = 0x0014, + Int_Mask = 0x0018, + Int_Status = 0x001C, + Fb_Base = 0x0100, + Fb_Line_Length = 0x0104, + Fb_Line_Count = 0x0108, + Fb_Line_Pitch = 0x010C, + Bus_Options = 0x0110, + V_Sync = 0x0200, + V_Back_Porch = 0x0204, + V_Data = 0x0208, + V_Front_Porch = 0x020C, + H_Sync = 0x0210, + H_Back_Porch = 0x0214, + H_Data = 0x0218, + H_Front_Porch = 0x021C, + Polarities = 0x0220, + Command = 0x0230, + Pixel_Format = 0x0240, + Red_Select = 0x0244, + Green_Select = 0x0248, + Blue_Select = 0x024C }; + + /** Reset value for Bus_Options register */ + static const size_t BUS_OPTIONS_RESETV = 0x408; + + /** Reset value for Version register */ + static const size_t VERSION_RESETV = 0x1CDC0000; + + /** max number of outstanding DMA requests possible */ + static const size_t MAX_OUTSTANDING_DMA_REQ_CAPACITY = 16; + + /** max number of beats delivered in one dma burst */ + static const size_t MAX_BURST_LEN = 16; + + /** size of internal buffer in bytes */ + static const size_t PIXEL_BUFFER_CAPACITY = 2048; + + /** AXI port width in bytes */ + static const size_t AXI_PORT_WIDTH = 8; + + /** + * @name RegisterFieldLayouts + * Bit layout declarations for multi-field registers. + */ + /**@{*/ + BitUnion32(VersionReg) + Bitfield<7,0> version_minor; + Bitfield<15,8> version_major; + Bitfield<31,16> product_id; + EndBitUnion(VersionReg) + + BitUnion32(InterruptReg) + Bitfield<0> dma_end; + Bitfield<1> bus_error; + Bitfield<2> vsync; + Bitfield<3> underrun; + EndBitUnion(InterruptReg) + + BitUnion32(FbLineCountReg) + Bitfield<11,0> fb_line_count; + Bitfield<31,12> reserved_31_12; + EndBitUnion(FbLineCountReg) + + BitUnion32(BusOptsReg) + Bitfield<4,0> burst_len; + Bitfield<7,5> reserved_7_5; + Bitfield<11,8> max_outstanding; + Bitfield<31,12> reserved_31_12; + EndBitUnion(BusOptsReg) + + BitUnion32(TimingReg) + Bitfield<11,0> val; + Bitfield<31,12> reserved_31_12; + EndBitUnion(TimingReg) + + BitUnion32(PolaritiesReg) + Bitfield<0> vsync_polarity; + Bitfield<1> hsync_polarity; + Bitfield<2> dataen_polarity; + Bitfield<3> data_polarity; + Bitfield<4> pxlclk_polarity; + Bitfield<31,5> reserved_31_5; + EndBitUnion(PolaritiesReg) + + BitUnion32(CommandReg) + Bitfield<0> enable; + Bitfield<31,1> reserved_31_1; + EndBitUnion(CommandReg) + + BitUnion32(PixelFormatReg) + Bitfield<2,0> reserved_2_0; + Bitfield<4,3> bytes_per_pixel; + Bitfield<30,5> reserved_30_5; + Bitfield<31> big_endian; + EndBitUnion(PixelFormatReg) + + BitUnion32(ColorSelectReg) + Bitfield<4,0> offset; + Bitfield<7,5> reserved_7_5; + Bitfield<11,8> size; + Bitfield<15,12> reserved_15_12; + Bitfield<23,16> default_color; + Bitfield<31,24> reserved_31_24; + EndBitUnion(ColorSelectReg) + /**@}*/ + + /** + * @name HDLCDRegisters + * HDLCD register contents. + */ + /**@{*/ + VersionReg version; /**< Version register */ + InterruptReg int_rawstat; /**< Interrupt raw status register */ + InterruptReg int_clear; /**< Interrupt clear register */ + InterruptReg int_mask; /**< Interrupt mask register */ + InterruptReg int_status; /**< Interrupt status register */ + uint32_t fb_base; /**< Frame buffer base address register */ + uint32_t fb_line_length; /**< Frame buffer Line length register */ + FbLineCountReg fb_line_count; /**< Frame buffer Line count register */ + uint32_t fb_line_pitch; /**< Frame buffer Line pitch register */ + BusOptsReg bus_options; /**< Bus options register */ + TimingReg v_sync; /**< Vertical sync width register */ + TimingReg v_back_porch; /**< Vertical back porch width register */ + TimingReg v_data; /**< Vertical data width register */ + TimingReg v_front_porch; /**< Vertical front porch width register */ + TimingReg h_sync; /**< Horizontal sync width register */ + TimingReg h_back_porch; /**< Horizontal back porch width register */ + TimingReg h_data; /**< Horizontal data width register */ + TimingReg h_front_porch; /**< Horizontal front porch width reg */ + PolaritiesReg polarities; /**< Polarities register */ + CommandReg command; /**< Command register */ + PixelFormatReg pixel_format; /**< Pixel format register */ + ColorSelectReg red_select; /**< Red color select register */ + ColorSelectReg green_select; /**< Green color select register */ + ColorSelectReg blue_select; /**< Blue color select register */ + /** @} */ + + /** Pixel clock period */ + const Tick pixelClock; + + /** VNC server */ + VncInput *vnc; + + /** Helper to write out bitmaps */ + Bitmap *bmp; + + /** Picture of what the current frame buffer looks like */ + std::ostream *pic; + + /** + * Event wrapper for dmaDone() + * + * This event call pushes its this pointer onto the freeDoneEvent vector + * and calls dmaDone() when triggered. While most of the time the burst + * length of a transaction will be the max burst length set by the driver, + * any trailing bytes must be handled with smaller lengths thus requiring + * the configurable burst length option. + */ + class DmaDoneEvent : public Event + { + private: + /** Reference to HDLCD that issued the corresponding DMA transaction */ + HDLcd &obj; + + /** Transaction size */ + size_t transSize; + + public: + /** + * Constructor. + * + * @param _obj HDLCD that issued the corresponding DMA transaction + */ + DmaDoneEvent(HDLcd *_obj) + : Event(), obj(*_obj), transSize(0) {} + + /** + * Sets the size of this transaction. + * + * @param len size of the transaction in bytes + */ + void setTransactionSize(size_t len) { + transSize = len; + } + + /** + * Gets the size of this transaction. + * + * @return size of this transaction in bytes + */ + size_t getTransactionSize() const { + return transSize; + } + + void process() { + obj.dmaDone(this); + } + + const std::string name() const { + return obj.name() + ".DmaDoneEvent"; + } + }; + + /** Start time for frame buffer dma read */ + Tick frameReadStartTime; + + /** Starting address for the current frame */ + Addr dmaStartAddr; + + /** Next address the dma should read from */ + Addr dmaCurAddr; + + /** One byte past the address of the last byte the dma should read + * from */ + Addr dmaMaxAddr; + + /** Number of pending dma reads */ + size_t dmaPendingNum; + + /** Flag indicating whether current frame has underrun */ + bool frameUnderrun; + + /** HDLcd virtual display buffer */ + uint8_t *virtualDisplayBuffer; + + /** Size of the pixel buffer */ + size_t pixelBufferSize; + + /** Index of the next pixel to render */ + size_t pixelIndex; + + /** Flag indicating whether video parameters need updating */ + bool doUpdateParams; + + /** Flag indicating whether a frame read / display is in progress */ + bool frameUnderway; + + /** + * Number of bytes in flight from DMA that have not reached the pixel + * buffer yet + */ + uint32_t dmaBytesInFlight; + + /** + * Gets the number of oustanding DMA transactions allowed on the bus at a + * time. + * + * @return gets the driver-specified number of outstanding DMA transactions + * from the hdlcd controller that are allowed on the bus at a time + */ + inline uint16_t maxOutstandingDma() const { + return bus_options.max_outstanding; + } + + /** + * Gets the number of bytes free in the pixel buffer. + * + * @return number of bytes free in the internal pixel buffer + */ + inline uint32_t bytesFreeInPixelBuffer() const { + return PIXEL_BUFFER_CAPACITY - (pixelBufferSize + dmaBytesInFlight); + } + + /** + * Gets the number of beats-per-burst for bus transactions. + * + * @return number of beats-per-burst per HDLcd DMA transaction + */ + inline size_t dmaBurstLength() const { + assert(bus_options.burst_len <= MAX_BURST_LEN); + return bus_options.burst_len; + } + + /** + * Gets the number of bytes per pixel. + * + * @return bytes per pixel + */ + inline size_t bytesPerPixel() const { + return pixel_format.bytes_per_pixel + 1; + } + + /** + * Gets frame buffer width. + * + * @return frame buffer width (pixels per line) + */ + inline size_t width() const { + return fb_line_length / bytesPerPixel(); + } + + /** + * Gets frame buffer height. + * + * @return frame buffer height (lines per panel) + */ + inline size_t height() const { + return fb_line_count.fb_line_count; + } + + /** + * Gets the total number of pixel clocks per display line. + * + * @return number of pixel clocks per display line including porch delays + * and horizontal sync time + */ + inline uint64_t PClksPerLine() const { + return h_back_porch.val + 1 + + h_data.val + 1 + + h_front_porch.val + 1 + + h_sync.val + 1; + } + + /** Send updated parameters to the vnc server */ + void updateVideoParams(bool unserializing); + + /** Generates an interrupt */ + void generateInterrupt(); + + /** Start reading the next frame */ + void startFrame(); + + /** End of frame reached */ + void endFrame(); + + /** Generate DMA read requests from frame buffer into pixel buffer */ + void fillPixelBuffer(); + + /** DMA done event */ + void dmaDone(DmaDoneEvent *event); + + /** Called when it is time to render a pixel */ + void renderPixel(); + + /** Start of frame event */ + EventWrapper<HDLcd, &HDLcd::startFrame> startFrameEvent; + + /** End of frame event */ + EventWrapper<HDLcd, &HDLcd::endFrame> endFrameEvent; + + /** Pixel render event */ + EventWrapper<HDLcd, &HDLcd::renderPixel> renderPixelEvent; + + /** Fill fifo */ + EventWrapper<HDLcd, &HDLcd::fillPixelBuffer> fillPixelBufferEvent; + + /** Wrapper to create an event out of the interrupt */ + EventWrapper<HDLcd, &HDLcd::generateInterrupt> intEvent; + + /**@{*/ + /** + * All pre-allocated DMA done events + * + * The HDLCD model preallocates maxOutstandingDma() number of + * DmaDoneEvents to avoid having to heap allocate every single + * event when it is needed. In order to keep track of which events + * are in flight and which are ready to be used, we use two + * different vectors. dmaDoneEventAll contains <i>all</i> + * DmaDoneEvents that the object may use, while dmaDoneEventFree + * contains a list of currently <i>unused</i> events. When an + * event needs to be scheduled, the last element of the + * dmaDoneEventFree is used and removed from the list. When an + * event fires, it is added to the end of the + * dmaEventFreeList. dmaDoneEventAll is never used except for in + * initialization and serialization. + */ + std::vector<DmaDoneEvent> dmaDoneEventAll; + + /** Unused DMA done events that are ready to be scheduled */ + std::vector<DmaDoneEvent *> dmaDoneEventFree; + /**@}*/ + + public: + typedef HDLcdParams Params; + + const Params * + params() const + { + return dynamic_cast<const Params *>(_params); + } + HDLcd(const Params *p); + ~HDLcd(); + + virtual Tick read(PacketPtr pkt); + virtual Tick write(PacketPtr pkt); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + /** + * Determine the address ranges that this device responds to. + * + * @return a list of non-overlapping address ranges + */ + AddrRangeList getAddrRanges() const; +}; + +#endif |