You won't have enough time to refresh everything at that speed.
The DRAM is not tested to check—nor does Micron warrant compliance with—normal mode timings or functionality when the DLL is disabled. An attempt has been made to have the DRAM operate in the normal mode where reasonably possible when the DLL has been disabled; however, by industry standard, a few known exceptions are defined:
• ODT is not allowed to be used.
• The output data is no longer edge-aligned to the clock.
• CL and CWL can only be six clocks.
When the DLL is disabled, timing and functionality can vary from the normal operation specifications when the DLL is enabled (see DLL Disable Mode in Commands section of the data sheet for more information). Disabling the DLL also implies the need to change the clock frequency (see Input Clock Frequency Change section of the data sheet for details).
EDIT: Just a thought, but what about a general FILL function? Specify a point coordinate and a colour and off it goes, filling in all pixels of the colour at the point coordinate with the new colour, until bounded by other colours? Or would that be too far?You only call the function twice in a row...
EG,Code: [Select]; *************************************************************************************
; ** Draw a filled ellipse (250,250)-(300,350) with palette color 9
; *************************************************************************************
set_x 0,d'175' ; set x0 register to 250 - The ellipse's top-left X position
set_y 0,d'175' ; set y0 register to 250 - The ellipse's top-left Y position
set_x 1,d'300' ; set x1 register to 300 - The ellipse's bottom-right X position
set_y 1,d'350' ; set y1 register to 350 - The ellipse's bottom-right Y position
plot_circle_fill d'9' ; plot a filled ellipse with palette color 9
plot_circle d'15' ; plot the outline of the same ellipse with palette color 15
Remember, my commands holding their screen coordinates were optimized to do this sort of thing.
You may make such a command in your Z80 graphics driver, but, it would just execute the above code.
The Geometry unit will draw between 25 million all the way up to 125 million pixels a second. It wont break a sweat drawing the extra few pixels surrounding the edge of an ellipse, or rectangle a second time.
Give it a test in geo.bas.
Now, truly begin the 'geometry_xy_plotter.sv'. Start with being able to load it's internal storage registers with the command input port.
Sorry, my bad. You meant a general 'Flood Fill', right?
Ok, that would take a little feedback cooperation between the 'geometry_xy_plotter.sv' and 'pixel_writer', but I'm wondering how I could generate a cheap method where it can properly seek and fill around the corner of an onscreen object.
A radiant style fill would be easy, but, on screen objects would be able to cast shadows which means it wouldn't be a true 'Flood Fill'. If a shadow is cast, you would need to manually start an additional 'Flood Fill' in the void to complete the fill which may once again still contain additional voids. If I can figure out an easy way to automatically drive the filling of the voids, you would have a really fast 'Flood Fill'. Otherwise, the fill will need to be done in a much slower maze-seek algorithm. (Much slower means filling something like 5 million pixels a second instead of 125 million a second.)
Also I wonder if your existing core would be much simpler had it worked with full color objects and surfaces as opposed to packed-color ones, as I assume you will need additional HW to pack and unpack pixel information from data bytes stored in memory. While in case of full color this will be a trivial memory address calculation without any need to somehow transform data you get from memory.
The other idea that came out of it was polygons. I think you mentioned being able to draw triangles or polygons with the ellipse function? Being able to draw bespoke shapes with three, four or potentially more, vertices either as wireframe or filled would be VERY useful. I know the line function can do the same thing with some additional work from the Z80, but being able to fill those shapes would be nice.
Yes, currently we have multiple different packed color modes and the pixel writer after the geometry unit has code to deal with it. Yes, 32 bit only removes a lot of glut, but, remember DDR3 smallest burst has 8 words @ 32 bit for the optimum. To make efficient use of the dram when reading and writing pixels, internally we would have a 8x32 = 256 bit bus which would look equivilant to a 1 bitplane image with an 8 bit memory bus. Yes, it would be written out a little different, but it boils down to the same idea. When reading or writing adjacent pixels, you do not want to sacrifice that minimum 4 cycle burst of the same DRAM memory address 8 times in a row with the write mask set for 1 individual byte every time. Now you may have a DDR3 ram controller with a smart cache to manage this, but to properly weave your pixel plotting commands with reads for the screen refresh, you should organize your memory requests on the page burst boundary with the bottom CAS A0,A1,A2 all = 0. Otherwise, there will end up having a few access penalties when as you cross a row address if the word alignment isn't on centered on the 8 byte burst. In fact, once you have done this, everything, like geometry command arrays should all be padded and sit on an 8 word boundary.
Otherwise, there will end up having a few access penalties when as you cross a row address
Otherwise, there will end up having a few access penalties when as you cross a row address
If your addressing scheme is such that the adjacent rows are in different banks, then crossing row boundaries will not pose any additional penalties.
Now, truly begin the 'geometry_xy_plotter.sv'. Start with being able to load it's internal storage registers with the command input port.
Use the maggie.sv and rs232_DEBUGGER.v to get an idea of how to associate/assign labels from the source command & how to store those labels into the memory registers. The labels and registers should be almost identical to the geo.bas labels. Except for the x# & y#, make them 2 dimensional 12 bit registers for ease of design. (I should have done that anyways within geo.bas. Sorry, my bad.
For example, if you need to begin to read right at the end of the row, all addresses '1's, even though the ram will do it, the 8 word burst will just wrap around for the remaining 7 cycles after you get that first word you want, and the remainder of that burst will be wasted when what you want right after that initial last word is the next row right after.
Now, truly begin the 'geometry_xy_plotter.sv'. Start with being able to load it's internal storage registers with the command input port.
Use the maggie.sv and rs232_DEBUGGER.v to get an idea of how to associate/assign labels from the source command & how to store those labels into the memory registers. The labels and registers should be almost identical to the geo.bas labels. Except for the x# & y#, make them 2 dimensional 12 bit registers for ease of design. (I should have done that anyways within geo.bas. Sorry, my bad.
I'd be lying if I said I knew what I was doing, so I've thrown some code at the Quartus project and I'm hoping what stuck is vaguely going in the right direction..
Have got as far as the functions (I think) - not sure how to progress. Anyway, take a look at the attached and let me know where I've gone wrong so far.
EDIT:
Updated code as I hadn't created registers for max_x, max_y or the two collision counters.
output Write_col[7:0], // An 8 bit saturation counter which counts the number of pixel write collisions
output Copy_col[7:0], // An 8 bit saturation counter which counts the number of blit write from read pixel write collisions
output idle // An output which goes high when the geometry plotter is finished and is doing nothing
draw_cmd_func[3:0]
draw_cmd_data_color[7:0]
draw_cmd_data_word_Y[11:0]
draw_cmd_data_word_X[11:0]
draw_cmd_tx
Then I would assign the output port wire to them:draw_cmd[35:32] = draw_cmd_func[3:0]
draw_cmd[31:24] = draw_cmd_data_color[7:0]
draw_cmd[23:12] = draw_cmd_data_word_Y[11:0]
draw_cmd[11:0] = draw_cmd_data_word_X[11:0]
draw_cmd_rdy = draw_cmd_tx
begin
draw_cmd_func <= CMD_OUT_#choose a function name#[3:0];
draw_cmd_data_color <= 'usually the draw color'
draw_cmd_data_word_Y <= 'usually Y coordinate'
draw_cmd_data_word_X <= 'usually X coordinate'
draw_cmd_tx <= 1;
end ...
else if no other new commands running draw_cmd_tx <=0 ;
Sorry, bit confused - how does draw_cmd[35:0] get populated with data? draw_cmd_func, draw_cmd_data_color, draw_cmd_data_word_Y and draw_cmd_data_word_X don't get set anywhere? I've obviously missed something...
EDIT: Yes - I missed the last code paragraph in your reply. Still not sure which X/Y coordinate gets set though?
//************************************************
// Assign output port wires to internal registers
//************************************************
assign draw_cmd[35:32] = draw_cmd_func[3:0];
assign draw_cmd[31:24] = draw_cmd_data_color[7:0];
assign draw_cmd[23:12] = draw_cmd_data_word_Y[11:0];
assign draw_cmd[11:0] = draw_cmd_data_word_X[11:0];
assign draw_cmd_rdy = draw_cmd_tx;
So I'm removing the func3 checks from Geo.bas entirely and instead checking func2 values, which need to match those in the Verilog file, right?
Did you include the code for 8'd127 in the extend_cmd case statement as an example of how the rest should go, or....? Bit confused as Geo.bas uses x0 * 4096 + y0, but in the Verilog you've just assigned x[0] and y[0] directly to draw_cmd_data_word_X/Y. Is that right?
Ok, I touched up the geometry_xy_plotter.sv.
Read it carefully and make it work and simulate in Quartus.
Also, add an IF(reset) ... else ... right at the top to default all the registers.
I also added the geo_shape, geo_run, geo_mask, geo_fill registers, tied them into the case statement and made a separate section after the loading of a command, the area where we place the running geo units. I also assigned the load_cmd output and commented on each reg's purpose.
I've added the box & box_filled command, however, I deliberately did not make any comments on the function.
It will be your job to fill in the comments for each line & post them here so I can make sure you understand what's going on.
case (geo_shape) // run a selected geometric drawing engine
4'd1 : begin // draw a filled rectangle
if ( geo_y != (y[1] + geo_ydir) ) begin // Check for bottom (or top, depending on sign of geo_ydir) of rectangle reached
if ( geo_x != (x[1] + geo_xdir) ) begin // Check for right (or left, depending on sign of geo_xdir) of rectangle reached
draw_cmd_func <= CMD_OUT_PXWRI[3:0]; // Set up command to pixel plotter to write a pixel,
draw_cmd_data_color <= geo_color; // ... in geo_colour,
draw_cmd_data_word_Y <= geo_y ; // ... at Y-coordinate,
draw_cmd_data_word_X <= geo_x ; // ... and X-coordinate.
draw_cmd_tx <= 1'b1; // send command (have moved this after the X, Y setting for easy reading)
// Now increment (or decrement according to geo_xdir) geo_x to the next pixel.
// If geo_fill is HIGH, step to next X-position to fill the rectangle.
// If geo_x is at the end edge, step past it.
// If geo_y is at start or end (top or bottom edge), step to next X-position to draw the horizontal line.
if (geo_fill || geo_x == x[1] || geo_y == y[0] || geo_y == y[1] ) geo_x <= geo_x + geo_xdir;
// Otherwise, jump to end X-position to draw other edge for non-filled rectangle.
else geo_x <= x[1];
end else begin // geo_x has passed vertical edge
draw_cmd_tx <= 1'b0; // do not send a draw cmd this cycle
geo_x <= x[0]; // reset X to start X-position
geo_y <= geo_y + geo_ydir; // increment (or decrement) Y-position for next line
end
end else begin // geo_y has passed horizontal edge
geo_run <= 1'b0; // stop geometry engine - shape completed
draw_cmd_tx <= 1'b0; // do not send a draw cmd this cycle
end
end // draw a filled rectangle
The code appears to simulate fine, with coordinates in all 4 directions.
You will also be responsible for testing the code in the simulator by manipulating the commands going into the geometry unit and deciphering the results.
I'm confused about how cmd_h (with a value of 12) creates a filled rectangle (or a value of 8 creates a non-filled rectangle) - or how any value is interpreted from cmd_h and cmd_l. There's a few bit operations and splitting of the cmd bus going on that I'm having difficulty following.
8'b000????? : begin // this range of commands all begin drawing a shape.
// drawing commands begin here. Keep the convention that:
// extend_cmd[0] = mask enable
// extend_cmd[1] = use the color in the copy/paste buffer. This one is for drawing in true color mode.
// extend_cmd[2] = fill enable
geo_shape[3] <= 1'b0; // geo shapes 0 through 7, geo shapes 8 through 15 are for copy & paste.
/*LINE 214*/ geo_shape[2:0] <= command_in[5:3];// Set which one of shapes 0 through 7 should be drawn. Shape 0 turns means nothing is being drawn
geo_fill <= command_in[2];
geo_paste <= command_in[1]; // used for drawing in true color 16 bit mode
geo_mask <= command_in[0];
geo_run <= 1'b1; // a flag which signifies that a geometric shap drawing engine will begin drawing
geo_color <= command_data8; // set the 8bit pen color.
geo_sub_func1 <= 4'b0; // for geometric engines which have multiple phases, reset the phase counter
geo_sub_func2 <= 4'b0; // for geometric engines which have 2 dimensional multiple phases, reset the phase counter
// Initialize the geometry unit starting coordinates and direction so it can begin plotting immediately
geo_x <= x[0]; // initialize the beginning pixel location
geo_y <= y[0]; // initialize the beginning pixel location
if ( x[1] < x[0] ) geo_xdir <= 12'd0-12'd1; // set the direction of the counter (negative x in this case)
else geo_xdir <= 12'd1; // positive x direction
if ( y[1] < y[0] ) geo_ydir <= 12'd0-12'd1; // negative y direction
else geo_ydir <= 12'd1; // positive y direction
end
8'b000????? : begin // this range of commands all begin drawing a shape.
// drawing commands begin here. Keep the convention that:
// ***extend_cmd[3] = fill enable
// ***extend_cmd[4] = use the color in the copy/paste buffer. This one is for drawing in true color mode.
// ***extend_cmd[5] = mask enable - when drawing, the mask colors will not be plotted as they are transparent
geo_shape[3] <= 1'b0; // geo shapes 0 through 7, geo shapes 8 through 15 are for copy & paste.
/*LINE214*/ geo_shape[2:0] <= command_in[2:0];// *** Set which one of shapes 0 through 7 should be drawn. Shape 0 turns means nothing is being drawn
geo_fill <= command_in[3]; // *** Fill enable bit.
geo_paste <= command_in[4]; // *** used for drawing in true color 16 bit mode
geo_mask <= 1'b0; // *** Mask disables when drawing raw geometry shapes
geo_run <= 1'b1; // a flag which signifies that a geometric shap drawing engine will begin drawing
geo_color <= command_data8; // set the 8bit pen color.
geo_sub_func1 <= 4'b0; // for geometric engines which have multiple phases, reset the phase counter
geo_sub_func2 <= 4'b0; // for geometric engines which have 2 dimensional multiple phases, reset the phase counter
// Initialize the geometry unit starting coordinates and direction so it can begin plotting immediately
geo_x <= x[0]; // initialize the beginning pixel location
geo_y <= y[0]; // initialize the beginning pixel location
if ( x[1] < x[0] ) geo_xdir <= 12'd0-12'd1; // set the direction of the counter (negative x in this case)
else geo_xdir <= 12'd1; // positive x direction
if ( y[1] < y[0] ) geo_ydir <= 12'd0-12'd1; // negative y direction
else geo_ydir <= 12'd1; // positive y direction
end
Change the .sv and simulate. Tell me now what are the new 2 commands for drawing a box outline and filled box. Simulate to make sure you got the figures correct.
This setup should make more sense...
Also make sure you verify that you understand how lines 101 & 102 work. Why those 2 allow you to set all the x#&y# with 12 bit numbers, and why lines 107-108 & 115-116 send out all 4 of each as destination or source memory addresses.
4'd2 : begin // draw a filled rectangle
4'd1 : begin // draw line from (x[0],y[0]) to (x[1],y[1])
.......
end // draw line from x[0],y[0] to x[1],y[1]