tiler/Tiler: start of tiling engine.
Some internal modules are written, with tests. Top-level tiler module still TODO.
This commit is contained in:
parent
e87e652efa
commit
ae90803026
|
@ -0,0 +1,414 @@
|
||||||
|
package Tiler;
|
||||||
|
|
||||||
|
import FIFOF::*;
|
||||||
|
import SpecialFIFOs::*;
|
||||||
|
import StmtFSM::*;
|
||||||
|
import ConfigReg::*;
|
||||||
|
import GetPut::*;
|
||||||
|
import ClientServer::*;
|
||||||
|
|
||||||
|
import VRAM::*;
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// Coordinates and coordinate transforms
|
||||||
|
|
||||||
|
// Within the tiler, the universe is a grid of tiles. The screen is a
|
||||||
|
// viewport that is placed onto that grid for rendering. If the
|
||||||
|
// viewport overflows the tile grid, the tile grid is wrapped and
|
||||||
|
// repeated until the entire viewport is on solid ground.
|
||||||
|
|
||||||
|
// PixelCoord is the X or Y coordinate of a pixel on screen.
|
||||||
|
typedef UInt#(10) PixelCoord;
|
||||||
|
|
||||||
|
// Scanline is the vertical offset of one display scanline. Scanline 0
|
||||||
|
// is the first line of pixels drawn at the top of a frame, scanline
|
||||||
|
// 479 is the final line at the bottom of the screen (assuming a
|
||||||
|
// display mode that uses full resolution, otherwise the final
|
||||||
|
// scanline will be a lower number).
|
||||||
|
typedef UInt#(9) Scanline;
|
||||||
|
|
||||||
|
// TileCoord is the X or Y coordinate of a tile.
|
||||||
|
typedef UInt#(8) TileCoord;
|
||||||
|
|
||||||
|
// TilePixelCoord is the X or Y coordinate of a pixel within a tile.
|
||||||
|
typedef UInt#(4) TilePixelCoord;
|
||||||
|
|
||||||
|
// TileSubcoord is like a TilePixelCoord, but expressed as a TileCoord
|
||||||
|
// plus a pixel offset within the tile.
|
||||||
|
typedef struct {
|
||||||
|
TileCoord tile;
|
||||||
|
TilePixelCoord pixel;
|
||||||
|
} TileSubcoord deriving (Bits, Eq, FShow);
|
||||||
|
|
||||||
|
// ExtendedPixelCoord is the X or Y coordinate of a pixel in tile
|
||||||
|
// space. This is a much larger space than PixelCoord, because in the
|
||||||
|
// maximal configuration the tiler can be working with 256 tiles of 16
|
||||||
|
// pixels, 4096 pixels compared to the screen's 640x480.
|
||||||
|
typedef UInt#(TAdd#(SizeOf#(TileCoord), SizeOf#(TilePixelCoord))) ExtendedPixelCoord;
|
||||||
|
|
||||||
|
// TileCount is a number of tiles. It exists as a distinct type from
|
||||||
|
// TileCoord because it needs to be 1 bit larger to be able to
|
||||||
|
// represent the value of zero tiles (which in TileCoord is just the
|
||||||
|
// identifier of the first tile).
|
||||||
|
typedef UInt#(TAdd#(SizeOf#(TileCoord), 1)) TileCount;
|
||||||
|
typedef UInt#(TAdd#(SizeOf#(TilePixelCoord), 1)) TilePixelCount;
|
||||||
|
typedef PixelCoord PixelCount;
|
||||||
|
|
||||||
|
// pixelcoord_to_tilesubcoord translates a screen pixel X or Y
|
||||||
|
// coordinate to the corresponding tile and pixel offset within the
|
||||||
|
// tile.
|
||||||
|
//
|
||||||
|
// This can be used for both row and column computations. When
|
||||||
|
// screen_pixel is a row number and the other parameters describe
|
||||||
|
// heights (tile height, map height, vertical scroll offset), the
|
||||||
|
// returned value identifies the corresponding tile row, as well as
|
||||||
|
// the line offset within the tile.
|
||||||
|
function TileSubcoord pixelcoord_to_tilesubcoord(PixelCoord screen_pixel, TilePixelCount pixels_per_tile, TileCount num_tiles, ExtendedPixelCoord scroll_offset);
|
||||||
|
ExtendedPixelCoord scrolled_pixel = scroll_offset + extend(screen_pixel);
|
||||||
|
TileCoord scrolled_tile = truncate(scrolled_pixel / extend(pixels_per_tile));
|
||||||
|
TileCoord wrapped_tile = truncate(extend(scrolled_tile) % num_tiles);
|
||||||
|
TilePixelCoord line_in_tile = truncate(scrolled_pixel % extend(pixels_per_tile));
|
||||||
|
return TileSubcoord{tile: wrapped_tile, pixel: line_in_tile};
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
// PaletteIndex is an index into the screen palette, describing the
|
||||||
|
// colour of one pixel.
|
||||||
|
typedef UInt#(8) PaletteIndex;
|
||||||
|
|
||||||
|
typedef UInt#(10) TileIndex;
|
||||||
|
|
||||||
|
// TilerMode is the tiling engine's output mode.
|
||||||
|
typedef enum {
|
||||||
|
// In Tile mode, the tiler operates on a tile map: for each
|
||||||
|
// {8,16}x{8,16} chunk of screen space, the map points to the tile
|
||||||
|
// to be rendered. The map entry combined with the corresponding
|
||||||
|
// tile descriptor defines the PaletteIndex to output for each
|
||||||
|
// pixel.
|
||||||
|
Tile,
|
||||||
|
// In Bitmap mode, the tiler operates on a simple bitmap array,
|
||||||
|
// where each array element directly describes the PaletteIndex for
|
||||||
|
// one pixel.
|
||||||
|
Bitmap
|
||||||
|
} TilerMode deriving (Bits, Eq, FShow);
|
||||||
|
|
||||||
|
// ColorDepth describes the number of bits used to encode a
|
||||||
|
// PaletteIndex in the tile or bitmap data.
|
||||||
|
typedef enum {
|
||||||
|
// 1bpp, each pixel can be on (foreground PaletteIndex) or off
|
||||||
|
// (background or transparent PaletteIndex, depending on other mode
|
||||||
|
// settings).
|
||||||
|
Bpp1,
|
||||||
|
// 2bpp, PaletteIndex values 0-3
|
||||||
|
Bpp2,
|
||||||
|
// 4bpp, PaletteIndex value 0-15
|
||||||
|
Bpp4,
|
||||||
|
// 8bpp, PaletteIndex value 0-255 (full range)
|
||||||
|
Bpp8
|
||||||
|
} ColorDepth deriving (Bits, Eq);
|
||||||
|
|
||||||
|
instance FShow#(ColorDepth);
|
||||||
|
function Fmt fshow(ColorDepth val);
|
||||||
|
case (val)
|
||||||
|
Bpp1: return $format("1bpp");
|
||||||
|
Bpp2: return $format("2bpp");
|
||||||
|
Bpp4: return $format("4bpp");
|
||||||
|
Bpp8: return $format("8bpp");
|
||||||
|
endcase
|
||||||
|
endfunction
|
||||||
|
endinstance
|
||||||
|
|
||||||
|
function UInt#(4) pixels_per_byte(ColorDepth bpp);
|
||||||
|
case (bpp)
|
||||||
|
Bpp1: return 8;
|
||||||
|
Bpp2: return 4;
|
||||||
|
Bpp4: return 2;
|
||||||
|
Bpp8: return 1;
|
||||||
|
endcase
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function UInt#(4) bits_per_pixel(ColorDepth bpp);
|
||||||
|
case (bpp)
|
||||||
|
Bpp1: return 1;
|
||||||
|
Bpp2: return 2;
|
||||||
|
Bpp4: return 4;
|
||||||
|
Bpp8: return 8;
|
||||||
|
endcase
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
// MapSize is the X or Y dimension of a tile map.
|
||||||
|
typedef enum {
|
||||||
|
Tiles32,
|
||||||
|
Tiles64,
|
||||||
|
Tiles128,
|
||||||
|
Tiles256
|
||||||
|
} MapSize deriving (Bits, Eq);
|
||||||
|
|
||||||
|
instance FShow#(MapSize);
|
||||||
|
function Fmt fshow(MapSize sz);
|
||||||
|
case (sz)
|
||||||
|
Tiles32: return $format("32 tiles");
|
||||||
|
Tiles64: return $format("64 tiles");
|
||||||
|
Tiles128: return $format("128 tiles");
|
||||||
|
Tiles256: return $format("256 tiles");
|
||||||
|
endcase
|
||||||
|
endfunction
|
||||||
|
endinstance
|
||||||
|
|
||||||
|
// // num_tiles returns the number of tiles for the given MapSize.
|
||||||
|
// function TileCount num_tiles(MapSize m);
|
||||||
|
// return 32 << pack(m);
|
||||||
|
// endfunction
|
||||||
|
|
||||||
|
// // tile_bytes returns the number of bytes needed to store cnt map tile
|
||||||
|
// // descriptors in memory.
|
||||||
|
// function VRAMAddr map_tile_bytes(TileCount cnt);
|
||||||
|
// return 2 * extend(cnt);
|
||||||
|
// endfunction
|
||||||
|
|
||||||
|
// function VRAMAddr tile_addr(MapSize cols, TileCoord row, TileCoord col);
|
||||||
|
// return (extend(row) * fromInteger(tile_bytes_per_row(cols))) + (2*extend(col));
|
||||||
|
// endfunction
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// The tiler hardware itself.
|
||||||
|
|
||||||
|
|
||||||
|
(* always_ready *)
|
||||||
|
interface TilerConfig;
|
||||||
|
interface Reg#(TilerMode) mode;
|
||||||
|
interface Reg#(ColorDepth) depth; // 1/2/4/8bpp
|
||||||
|
interface Reg#(Bool) text_hicolor; // in 1bpp tile mode, 256 foreground colors and transparent background
|
||||||
|
interface Reg#(VRAMAddr) tile_base; // or bitmap data in bitmap mode
|
||||||
|
|
||||||
|
// Tile mode settings
|
||||||
|
interface Reg#(VRAMAddr) map_base;
|
||||||
|
interface Reg#(MapSize) map_width; // 32/64/128/256
|
||||||
|
interface Reg#(MapSize) map_height; // 32/64/128/256
|
||||||
|
interface Reg#(Bool) tile_double_width; // 8b or 16b tiles
|
||||||
|
interface Reg#(Bool) tile_double_height; // 8b or 16b tiles
|
||||||
|
interface Reg#(ExtendedPixelCoord) horizontal_scroll; // pixels from left screen edge
|
||||||
|
interface Reg#(ExtendedPixelCoord) vertical_scroll; // pixels from top screen edge
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module mkTilerConfig(TilerConfig);
|
||||||
|
Reg#(TilerMode) mode <- mkConfigReg(Tile);
|
||||||
|
Reg#(ColorDepth) depth <- mkConfigReg(Bpp1);
|
||||||
|
Reg#(Bool) text_hicolor <- mkConfigReg(False);
|
||||||
|
Reg#(VRAMAddr) tile_base <- mkConfigReg(0);
|
||||||
|
|
||||||
|
Reg#(VRAMAddr) map_base <- mkConfigReg(0);
|
||||||
|
Reg#(MapSize) map_width <- mkConfigReg(Tiles32);
|
||||||
|
Reg#(MapSize) map_height <- mkConfigReg(Tiles32);
|
||||||
|
Reg#(Bool) tile_double_width <- mkConfigReg(False);
|
||||||
|
Reg#(Bool) tile_double_height <- mkConfigReg(False);
|
||||||
|
Reg#(ExtendedPixelCoord) horizontal_scroll <- mkConfigReg(0);
|
||||||
|
Reg#(ExtendedPixelCoord) vertical_scroll <- mkConfigReg(0);
|
||||||
|
|
||||||
|
return (interface TilerConfig
|
||||||
|
interface mode = mode;
|
||||||
|
interface depth = depth;
|
||||||
|
interface text_hicolor = text_hicolor;
|
||||||
|
interface tile_base = tile_base;
|
||||||
|
interface map_base = map_base;
|
||||||
|
interface map_width = map_width;
|
||||||
|
interface map_height = map_height;
|
||||||
|
interface tile_double_width = tile_double_width;
|
||||||
|
interface tile_double_height = tile_double_height;
|
||||||
|
interface horizontal_scroll = horizontal_scroll;
|
||||||
|
interface vertical_scroll = vertical_scroll;
|
||||||
|
endinterface);
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
interface PaletteExpander;
|
||||||
|
method Action expand(Bit#(8) val, UInt#(4) num_pixels, ColorDepth bpp);
|
||||||
|
method ActionValue#(PaletteIndex) pixel();
|
||||||
|
method Bool done();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module mkPaletteExpander(PaletteExpander);
|
||||||
|
Reg#(Bit#(8)) val[2] <- mkCReg(2, 0);
|
||||||
|
Reg#(UInt#(4)) num_pixels[2] <- mkCReg(2, 0);
|
||||||
|
Reg#(ColorDepth) bpp <- mkReg(Bpp1);
|
||||||
|
|
||||||
|
method Action expand(val_in, num_pixels_in, bpp_in) if (num_pixels[1] == 0);
|
||||||
|
val[1] <= val_in;
|
||||||
|
num_pixels[1] <= num_pixels_in;
|
||||||
|
bpp <= bpp_in;
|
||||||
|
endmethod
|
||||||
|
|
||||||
|
method ActionValue#(PaletteIndex) pixel() if (num_pixels[0] > 0);
|
||||||
|
PaletteIndex ret = case (bpp)
|
||||||
|
Bpp1: extend(unpack(val[0][7]));
|
||||||
|
Bpp2: extend(unpack(val[0][7:6]));
|
||||||
|
Bpp4: extend(unpack(val[0][7:4]));
|
||||||
|
Bpp8: unpack(val[0]);
|
||||||
|
endcase;
|
||||||
|
val[0] <= val[0] << bits_per_pixel(bpp);
|
||||||
|
num_pixels[0] <= num_pixels[0] - 1;
|
||||||
|
return ret;
|
||||||
|
endmethod
|
||||||
|
|
||||||
|
method Bool done();
|
||||||
|
return num_pixels[0] == 0;
|
||||||
|
endmethod
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VRAMAddr tile_row_addr;
|
||||||
|
TilePixelCoord start_col;
|
||||||
|
TilePixelCoord end_col;
|
||||||
|
ColorDepth bpp;
|
||||||
|
Bool flip;
|
||||||
|
} TileRequest deriving (Bits, Eq, FShow);
|
||||||
|
|
||||||
|
instance DefaultValue#(TileRequest);
|
||||||
|
defaultValue = TileRequest{
|
||||||
|
tile_row_addr: 0,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 0,
|
||||||
|
bpp: Bpp1,
|
||||||
|
flip: False
|
||||||
|
};
|
||||||
|
endinstance
|
||||||
|
|
||||||
|
interface TileRenderer;
|
||||||
|
interface Server#(TileRequest, PaletteIndex) tiles;
|
||||||
|
interface Client#(VRAMRequest, VRAMResponse) vram;
|
||||||
|
|
||||||
|
method Bool done();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module mkTileRenderer(TileRenderer ifc);
|
||||||
|
// Render output. Memory handler below feeds bytes in, pixel data
|
||||||
|
// comes out.
|
||||||
|
PaletteExpander expander <- mkPaletteExpander();
|
||||||
|
|
||||||
|
// Memory fetcher state: number of bytes remaining to issue, issue
|
||||||
|
// address, whether rendering is going backwards through memory.
|
||||||
|
Reg#(UInt#(5)) bytes_to_issue <- mkReg(0); // 0 to 16
|
||||||
|
Reg#(VRAMAddr) next_byte_addr <- mkReg(0);
|
||||||
|
Reg#(Bool) flip <- mkReg(False);
|
||||||
|
|
||||||
|
// Memory reader state: number of responses remaining to process,
|
||||||
|
// initial and final shifts to apply.
|
||||||
|
Reg#(UInt#(5)) bytes_to_retire <- mkReg(0); // 0 to 16
|
||||||
|
Reg#(UInt#(4)) initial_skip_pixels <- mkReg(0);
|
||||||
|
Reg#(UInt#(4)) final_skip_pixels <- mkReg(0);
|
||||||
|
Reg#(ColorDepth) bpp <- mkReg(Bpp1);
|
||||||
|
|
||||||
|
function Bit#(8) reverse_pixels(Bit#(8) px);
|
||||||
|
case (bpp)
|
||||||
|
Bpp1: return reverseBits(px);
|
||||||
|
Bpp2: return {px[1:0], px[3:2], px[5:4], px[7:6]};
|
||||||
|
Bpp4: return {px[3:0], px[7:4]};
|
||||||
|
Bpp8: return px;
|
||||||
|
endcase
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function Bool mem_done();
|
||||||
|
return bytes_to_issue == 0 && bytes_to_retire == 0;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function VRAMAddr byte_for_pixel(TilePixelCoord pixel, ColorDepth depth);
|
||||||
|
return extend(pixel / pixels_per_byte(depth));
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
interface Client vram;
|
||||||
|
interface Get request;
|
||||||
|
method ActionValue#(VRAMRequest) get() if (bytes_to_issue > 0);
|
||||||
|
next_byte_addr <= flip ? next_byte_addr - 1 : next_byte_addr + 1;
|
||||||
|
bytes_to_issue <= bytes_to_issue - 1;
|
||||||
|
return VRAMRequest{
|
||||||
|
addr: next_byte_addr,
|
||||||
|
data: tagged Invalid
|
||||||
|
};
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface Put response;
|
||||||
|
method Action put(VRAMResponse resp) if (bytes_to_retire > 0);
|
||||||
|
let tile_byte = resp.data;
|
||||||
|
if (flip)
|
||||||
|
tile_byte = reverse_pixels(tile_byte);
|
||||||
|
|
||||||
|
|
||||||
|
let pixels_to_render = pixels_per_byte(bpp);
|
||||||
|
if (initial_skip_pixels != 0) begin
|
||||||
|
tile_byte = tile_byte << (initial_skip_pixels * bits_per_pixel(bpp));
|
||||||
|
pixels_to_render = pixels_to_render - initial_skip_pixels;
|
||||||
|
end
|
||||||
|
else if (bytes_to_retire == 1) // Final byte for this tile
|
||||||
|
pixels_to_render = pixels_to_render - final_skip_pixels;
|
||||||
|
|
||||||
|
expander.expand(tile_byte, pixels_to_render, bpp);
|
||||||
|
|
||||||
|
bytes_to_retire <= bytes_to_retire - 1;
|
||||||
|
initial_skip_pixels <= 0;
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface Server tiles;
|
||||||
|
interface Put request;
|
||||||
|
method Action put(tile_req) if (mem_done());
|
||||||
|
UInt#(4) px_per_byte = pixels_per_byte(tile_req.bpp);
|
||||||
|
|
||||||
|
// Depending on color depth and tile width, one row of a
|
||||||
|
// tile can span multiple bytes. Combined with horizontal
|
||||||
|
// scrolling, we may not be starting the render on the
|
||||||
|
// first byte of the tile data.
|
||||||
|
//
|
||||||
|
// Likewise when rendering the last tile in a scanline, we
|
||||||
|
// may cut off early, and thus need to calculate the
|
||||||
|
// ending byte as well.
|
||||||
|
VRAMAddr start_col_byte = byte_for_pixel(tile_req.start_col, tile_req.bpp);
|
||||||
|
VRAMAddr end_col_byte = byte_for_pixel(tile_req.end_col, tile_req.bpp);
|
||||||
|
// start and end are inclusive, hence the +1.
|
||||||
|
UInt#(5) bytes_to_read = truncate(end_col_byte - start_col_byte + 1);
|
||||||
|
|
||||||
|
// Memory operations happen on a byte basis, but again
|
||||||
|
// depending on color depth we may not start/end the tile
|
||||||
|
// render on a byte boundary. In practice, the tile render
|
||||||
|
// breaks down into 3 phases: render the first byte
|
||||||
|
// (potentially partially), then zero or more whole bytes,
|
||||||
|
// then the final byte (potentially partially).
|
||||||
|
//
|
||||||
|
// These values are the number of pixels to omit from
|
||||||
|
// those first and last bytes.
|
||||||
|
UInt#(4) start_skip_pixels = tile_req.start_col % px_per_byte;
|
||||||
|
UInt#(4) end_skip_pixels = px_per_byte - 1 - (tile_req.end_col % px_per_byte);
|
||||||
|
|
||||||
|
// One final wrinkle is that the tile may be flipped
|
||||||
|
// horizontally. If so, we need to start rendering from
|
||||||
|
// the final byte and move backwards, with the skipped
|
||||||
|
// pixel counts swapped appropriately.
|
||||||
|
//
|
||||||
|
// Here we're flipping the order in which we'll read the
|
||||||
|
// bytes. The retire_read rule above handles swapping the
|
||||||
|
// pixels within each byte, if applicable.
|
||||||
|
bytes_to_issue <= bytes_to_read;
|
||||||
|
bytes_to_retire <= bytes_to_read;
|
||||||
|
flip <= tile_req.flip;
|
||||||
|
bpp <= tile_req.bpp;
|
||||||
|
initial_skip_pixels <= start_skip_pixels;
|
||||||
|
final_skip_pixels <= end_skip_pixels;
|
||||||
|
if (tile_req.flip) begin
|
||||||
|
next_byte_addr <= tile_req.tile_row_addr + end_col_byte;
|
||||||
|
//initial_skip_pixels <= end_skip_pixels;
|
||||||
|
//final_skip_pixels <= start_skip_pixels;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
next_byte_addr <= tile_req.tile_row_addr + start_col_byte;
|
||||||
|
end
|
||||||
|
endmethod
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
interface response = toGet(expander.pixel);
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
method Bool done();
|
||||||
|
return mem_done() && expander.done();
|
||||||
|
endmethod
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,569 @@
|
||||||
|
package Tiler_Test;
|
||||||
|
|
||||||
|
import Connectable::*;
|
||||||
|
import GetPut::*;
|
||||||
|
import ClientServer::*;
|
||||||
|
import Assert::*;
|
||||||
|
import StmtFSM::*;
|
||||||
|
import LFSR::*;
|
||||||
|
import Probe::*;
|
||||||
|
|
||||||
|
import Testing::*;
|
||||||
|
import Tiler::*;
|
||||||
|
import VRAM::*;
|
||||||
|
|
||||||
|
interface Test;
|
||||||
|
method Action start();
|
||||||
|
method Bool done();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
(* synthesize *)
|
||||||
|
module mkTestPixelcoordToTileSubcoord(Test);
|
||||||
|
let testflags <- mkTestFlags();
|
||||||
|
Reg#(Bool) run <- mkReg(False);
|
||||||
|
Reg#(Bool) finished <- mkReg(False);
|
||||||
|
|
||||||
|
function Action check_pxc_to_tsc(PixelCoord screen_pixel, TilePixelCount pixels_per_tile, TileCount num_tiles, ExtendedPixelCoord scroll_offset, TileCoord want_tile, TilePixelCoord want_subtile);
|
||||||
|
return action
|
||||||
|
let got = pixelcoord_to_tilesubcoord(screen_pixel, pixels_per_tile, num_tiles, scroll_offset);
|
||||||
|
let want = TileSubcoord{tile: want_tile, pixel: want_subtile};
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("pixelcoord_to_tilesubcoord(", screen_pixel, ", ", pixels_per_tile, ", ", num_tiles, ", ", scroll_offset, ") = ", fshow(got), ", want ", fshow(want));
|
||||||
|
dynamicAssert(got == want, "wrong output from pixelcoord_to_tilesubcoord");
|
||||||
|
endaction;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
rule test (run && !finished);
|
||||||
|
// Basic conversion with 32x8px tiles
|
||||||
|
check_pxc_to_tsc(0, 8, 32, 0,
|
||||||
|
0, 0);
|
||||||
|
check_pxc_to_tsc(5, 8, 32, 0,
|
||||||
|
0, 5);
|
||||||
|
check_pxc_to_tsc(7, 8, 32, 0,
|
||||||
|
0, 7);
|
||||||
|
check_pxc_to_tsc(8, 8, 32, 0,
|
||||||
|
1, 0);
|
||||||
|
check_pxc_to_tsc(9, 8, 32, 0,
|
||||||
|
1, 1);
|
||||||
|
|
||||||
|
// 32x16px tiles
|
||||||
|
check_pxc_to_tsc(9, 16, 32, 0,
|
||||||
|
0, 9);
|
||||||
|
check_pxc_to_tsc(15, 16, 32, 0,
|
||||||
|
0, 15);
|
||||||
|
check_pxc_to_tsc(16, 16, 32, 0,
|
||||||
|
1, 0);
|
||||||
|
|
||||||
|
// Tile wraparound at 32x8
|
||||||
|
check_pxc_to_tsc(255, 8, 32, 0,
|
||||||
|
31, 7);
|
||||||
|
check_pxc_to_tsc(256, 8, 32, 0,
|
||||||
|
0, 0);
|
||||||
|
check_pxc_to_tsc(511, 8, 32, 0,
|
||||||
|
31, 7);
|
||||||
|
check_pxc_to_tsc(512, 8, 32, 0,
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
// Tile wraparound at 64x8
|
||||||
|
check_pxc_to_tsc(256, 8, 64, 0,
|
||||||
|
32, 0);
|
||||||
|
check_pxc_to_tsc(511, 8, 64, 0,
|
||||||
|
63, 7);
|
||||||
|
check_pxc_to_tsc(512, 8, 64, 0,
|
||||||
|
0, 0);
|
||||||
|
|
||||||
|
// Scroll offset
|
||||||
|
check_pxc_to_tsc(0, 8, 32, 42,
|
||||||
|
5, 2);
|
||||||
|
check_pxc_to_tsc(1, 8, 32, 42,
|
||||||
|
5, 3);
|
||||||
|
check_pxc_to_tsc(5, 8, 32, 42,
|
||||||
|
5, 7);
|
||||||
|
check_pxc_to_tsc(6, 8, 32, 42,
|
||||||
|
6, 0);
|
||||||
|
check_pxc_to_tsc(6, 8, 32, 41,
|
||||||
|
5, 7);
|
||||||
|
|
||||||
|
// Scroll offset vs. wraparound
|
||||||
|
check_pxc_to_tsc(255, 8, 32, 2,
|
||||||
|
0, 1);
|
||||||
|
check_pxc_to_tsc(0, 8, 32, 3072,
|
||||||
|
0, 0);
|
||||||
|
check_pxc_to_tsc(0, 8, 32, 3071,
|
||||||
|
31, 7);
|
||||||
|
|
||||||
|
finished <= True;
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method Action start() if (!run);
|
||||||
|
run <= True;
|
||||||
|
endmethod
|
||||||
|
method done = finished._read;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
(* synthesize *)
|
||||||
|
module mkTestPaletteExpander(Test);
|
||||||
|
let testflags <- mkTestFlags();
|
||||||
|
let cycles <- mkCycleCounter();
|
||||||
|
|
||||||
|
let dut <- mkPaletteExpander();
|
||||||
|
let got_probe <- mkProbe();
|
||||||
|
let got_done_probe <- mkProbe();
|
||||||
|
(* no_implicit_conditions,fire_when_enabled *)
|
||||||
|
rule probe_done;
|
||||||
|
got_done_probe <= dut.done;
|
||||||
|
endrule
|
||||||
|
|
||||||
|
function Action check_pixel(PaletteIndex want);
|
||||||
|
return action
|
||||||
|
let got <- dut.pixel();
|
||||||
|
got_probe <= got;
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("PaletteExpander.pixel() = %0d, want %0d", got, want);
|
||||||
|
dynamicAssert(got == want, "wrong PaletteExpander output");
|
||||||
|
|
||||||
|
let got_done = dut.done();
|
||||||
|
let want_done = False;
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("PaletteExpander.done() = ", fshow(got_done), ", want ", fshow(want_done));
|
||||||
|
dynamicAssert(got_done == want_done, "wrong PaletteExpander done()");
|
||||||
|
|
||||||
|
dynamicAssert(cycles == 1, "output took more than 1 cycle");
|
||||||
|
cycles.reset();
|
||||||
|
endaction;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let fsm <- mkFSM(par
|
||||||
|
seq
|
||||||
|
dut.expand('h8e, 8, Bpp1);
|
||||||
|
dut.expand('ha4, 4, Bpp2);
|
||||||
|
dut.expand('h6c, 2, Bpp4);
|
||||||
|
dut.expand('h4d, 1, Bpp8);
|
||||||
|
|
||||||
|
dut.expand('h8e, 5, Bpp1);
|
||||||
|
dut.expand('ha4, 3, Bpp2);
|
||||||
|
dut.expand('h6c, 1, Bpp4);
|
||||||
|
endseq
|
||||||
|
|
||||||
|
seq
|
||||||
|
cycles.reset();
|
||||||
|
|
||||||
|
// 0x8e, 1bpp
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
|
||||||
|
// 0xa4, 2bpp
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
|
||||||
|
// 0x6c, 4bpp
|
||||||
|
check_pixel(6);
|
||||||
|
check_pixel(12);
|
||||||
|
|
||||||
|
// 0x4d, 8bpp
|
||||||
|
check_pixel('h4d);
|
||||||
|
|
||||||
|
// 0x8d, 1bpp, first 5 only
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
// 0xa4, 2bpp, first 3 only
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
// 0x6c, 4bpp, first only
|
||||||
|
check_pixel(6);
|
||||||
|
|
||||||
|
action
|
||||||
|
dynamicAssert(dut.done, "PaletteExpander not done after processing all requests");
|
||||||
|
dynamicAssert(cycles == 1, "done flag took too long to assert");
|
||||||
|
endaction
|
||||||
|
endseq
|
||||||
|
|
||||||
|
endpar);
|
||||||
|
|
||||||
|
method start = fsm.start;
|
||||||
|
method done = fsm.done;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
(* synthesize *)
|
||||||
|
module mkTestTileRenderer(Test);
|
||||||
|
let testflags <- mkTestFlags();
|
||||||
|
let cycles <- mkCycleCounter();
|
||||||
|
|
||||||
|
let vram <- mkVRAM(4);
|
||||||
|
let dut <- mkTileRenderer();
|
||||||
|
mkConnection(dut.vram, vram.tile1);
|
||||||
|
|
||||||
|
let got_probe <- mkProbe();
|
||||||
|
let want_probe <- mkProbe();
|
||||||
|
let done_probe <- mkProbe();
|
||||||
|
(* no_implicit_conditions,fire_when_enabled *)
|
||||||
|
rule set_done_probe;
|
||||||
|
done_probe <= dut.done();
|
||||||
|
endrule
|
||||||
|
|
||||||
|
function Action check_pixel(PaletteIndex want);
|
||||||
|
return action
|
||||||
|
let got <- dut.tiles.response.get();
|
||||||
|
got_probe <= got;
|
||||||
|
want_probe <= want;
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("TileRenderer.get() = %02x, want %02x", got, want);
|
||||||
|
dynamicAssert(got == want, "wrong pixel rendered");
|
||||||
|
endaction;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
Reg#(VRAMAddr) i <- mkReg(0);
|
||||||
|
LFSR#(Bit#(8)) rnd <- mkLFSR_8();
|
||||||
|
let populate_vram = seq
|
||||||
|
for (i <= 0; i < 32; i <= i+1) action
|
||||||
|
let val = rnd.value();
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("VRAM.write(%0d, 0x%02x) (%0d, %08b)", i, val, val, val);
|
||||||
|
vram.cpu.request.put(VRAMRequest{
|
||||||
|
addr: i,
|
||||||
|
data: tagged Valid rnd.value
|
||||||
|
});
|
||||||
|
rnd.next();
|
||||||
|
endaction
|
||||||
|
endseq;
|
||||||
|
|
||||||
|
let test_basic_render = seq
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 1,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp1,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 0,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp1,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 2,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp2,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 2,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp4,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(4);
|
||||||
|
check_pixel(7);
|
||||||
|
check_pixel(10);
|
||||||
|
check_pixel(13);
|
||||||
|
check_pixel(13);
|
||||||
|
check_pixel(8);
|
||||||
|
check_pixel(6);
|
||||||
|
check_pixel(12);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 4,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp8,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel('hd8);
|
||||||
|
check_pixel('h6c);
|
||||||
|
check_pixel('h36);
|
||||||
|
check_pixel('h1b);
|
||||||
|
check_pixel('h83);
|
||||||
|
check_pixel('hcf);
|
||||||
|
check_pixel('he9);
|
||||||
|
check_pixel('hfa);
|
||||||
|
endseq;
|
||||||
|
|
||||||
|
let test_stalled_consumer = par
|
||||||
|
seq
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 12,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp8,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 4,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp8,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
endseq
|
||||||
|
|
||||||
|
seq
|
||||||
|
check_pixel('h7d);
|
||||||
|
check_pixel('hb0);
|
||||||
|
check_pixel('h58);
|
||||||
|
check_pixel('h2c);
|
||||||
|
repeat (10) noAction;
|
||||||
|
check_pixel('h16);
|
||||||
|
check_pixel('h0b);
|
||||||
|
check_pixel('h8b);
|
||||||
|
check_pixel('hcb);
|
||||||
|
|
||||||
|
repeat (5) noAction;
|
||||||
|
check_pixel('hd8);
|
||||||
|
check_pixel('h6c);
|
||||||
|
check_pixel('h36);
|
||||||
|
repeat (5) noAction;
|
||||||
|
check_pixel('h1b);
|
||||||
|
check_pixel('h83);
|
||||||
|
check_pixel('hcf);
|
||||||
|
check_pixel('he9);
|
||||||
|
check_pixel('hfa);
|
||||||
|
endseq
|
||||||
|
endpar;
|
||||||
|
|
||||||
|
let test_scrolled_start = seq
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 1,
|
||||||
|
start_col: 3,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp1,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 2,
|
||||||
|
start_col: 1,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp4,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(7);
|
||||||
|
check_pixel(10);
|
||||||
|
check_pixel(13);
|
||||||
|
check_pixel(13);
|
||||||
|
check_pixel(8);
|
||||||
|
check_pixel(6);
|
||||||
|
check_pixel(12);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 4,
|
||||||
|
start_col: 3,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp2,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(0);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 12,
|
||||||
|
start_col: 3,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp8,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel('h2c);
|
||||||
|
check_pixel('h16);
|
||||||
|
check_pixel('h0b);
|
||||||
|
check_pixel('h8b);
|
||||||
|
check_pixel('hcb);
|
||||||
|
endseq;
|
||||||
|
|
||||||
|
let test_scrolled_end = seq
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 1,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 5,
|
||||||
|
bpp: Bpp1,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 2,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 4,
|
||||||
|
bpp: Bpp4,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(4);
|
||||||
|
check_pixel(7);
|
||||||
|
check_pixel(10);
|
||||||
|
check_pixel(13);
|
||||||
|
check_pixel(13);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 4,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 4,
|
||||||
|
bpp: Bpp2,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 12,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 4,
|
||||||
|
bpp: Bpp8,
|
||||||
|
flip: False
|
||||||
|
});
|
||||||
|
check_pixel('h7d);
|
||||||
|
check_pixel('hb0);
|
||||||
|
check_pixel('h58);
|
||||||
|
check_pixel('h2c);
|
||||||
|
check_pixel('h16);
|
||||||
|
endseq;
|
||||||
|
|
||||||
|
let test_flipped_tile = seq
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 1,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp1,
|
||||||
|
flip: True
|
||||||
|
});
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 2,
|
||||||
|
start_col: 0,
|
||||||
|
end_col: 7,
|
||||||
|
bpp: Bpp2,
|
||||||
|
flip: True
|
||||||
|
});
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(1);
|
||||||
|
check_pixel(0);
|
||||||
|
check_pixel(1);
|
||||||
|
|
||||||
|
dut.tiles.request.put(TileRequest{
|
||||||
|
tile_row_addr: 2,
|
||||||
|
start_col: 1,
|
||||||
|
end_col: 5,
|
||||||
|
bpp: Bpp2,
|
||||||
|
flip: True
|
||||||
|
});
|
||||||
|
// pixels in VRAM order: 1 0 1 3 2 2 3 1
|
||||||
|
// unflipped slice : _ 0 1 3 2 2 _ _
|
||||||
|
// flipped pixels : 1 3 2 2 3 1 0 1
|
||||||
|
// flipped slice : _ 3 2 2 3 1 _ _
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(2);
|
||||||
|
check_pixel(3);
|
||||||
|
check_pixel(1);
|
||||||
|
endseq;
|
||||||
|
|
||||||
|
let fsm <- mkFSM(seq
|
||||||
|
populate_vram;
|
||||||
|
|
||||||
|
test_basic_render;
|
||||||
|
test_stalled_consumer;
|
||||||
|
test_scrolled_start;
|
||||||
|
test_scrolled_end;
|
||||||
|
test_flipped_tile;
|
||||||
|
|
||||||
|
repeat (10) noAction;
|
||||||
|
endseq);
|
||||||
|
|
||||||
|
method start = fsm.start;
|
||||||
|
method done = fsm.done;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module mkTB();
|
||||||
|
let test_pxc_to_tsc <- mkTestPixelcoordToTileSubcoord();
|
||||||
|
let test_palette_expander <- mkTestPaletteExpander();
|
||||||
|
let test_tile_renderer <- mkTestTileRenderer();
|
||||||
|
|
||||||
|
Reg#(Scanline) line <- mkReg(0);
|
||||||
|
runTest(2000,
|
||||||
|
mkTest("Tiler", seq
|
||||||
|
mkTest("Tiler/PixelcoordToTilesubcoord", seq
|
||||||
|
test_pxc_to_tsc.start();
|
||||||
|
await(test_pxc_to_tsc.done);
|
||||||
|
endseq);
|
||||||
|
mkTest("Tiler/PaletteExpander", seq
|
||||||
|
test_palette_expander.start();
|
||||||
|
await(test_palette_expander.done);
|
||||||
|
endseq);
|
||||||
|
mkTest("Tiler/TileRenderer", seq
|
||||||
|
test_tile_renderer.start();
|
||||||
|
await(test_tile_renderer.done);
|
||||||
|
endseq);
|
||||||
|
endseq));
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -14,6 +14,7 @@ import VRAMCore::*;
|
||||||
export VRAMAddr, VRAMData, VRAMRequest(..), VRAMResponse(..);
|
export VRAMAddr, VRAMData, VRAMRequest(..), VRAMResponse(..);
|
||||||
|
|
||||||
export VRAMServer(..);
|
export VRAMServer(..);
|
||||||
|
export VRAMClient(..);
|
||||||
export VRAM(..), mkVRAM;
|
export VRAM(..), mkVRAM;
|
||||||
|
|
||||||
export mkArbitratedVRAMServers;
|
export mkArbitratedVRAMServers;
|
||||||
|
@ -21,6 +22,9 @@ export mkArbitratedVRAMServers;
|
||||||
// A VRAMServer is a memory port.
|
// A VRAMServer is a memory port.
|
||||||
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
|
typedef Server#(VRAMRequest, VRAMResponse) VRAMServer;
|
||||||
|
|
||||||
|
// A VRAMClient is a user of a memory port.
|
||||||
|
typedef Client#(VRAMRequest, VRAMResponse) VRAMClient;
|
||||||
|
|
||||||
// mkArbitratedVRAMServers expands a VRAMServer port into multiple
|
// mkArbitratedVRAMServers expands a VRAMServer port into multiple
|
||||||
// ports through the use of a MemArbiter.
|
// ports through the use of a MemArbiter.
|
||||||
module mkArbitratedVRAMServers(VRAMServer ram, MemArbiter#(n, VRAMAddr) arb, Vector#(n, VRAMServer) ifc)
|
module mkArbitratedVRAMServers(VRAMServer ram, MemArbiter#(n, VRAMAddr) arb, Vector#(n, VRAMServer) ifc)
|
||||||
|
|
Loading…
Reference in New Issue