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 /src/dev/arm/hdlcd.hh | |
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.
Diffstat (limited to 'src/dev/arm/hdlcd.hh')
-rw-r--r-- | src/dev/arm/hdlcd.hh | 502 |
1 files changed, 502 insertions, 0 deletions
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 |