Free Essay

1-D Cellular Automaton on Fpga

In:

Submitted By cavendishli
Words 4749
Pages 19
| Lab 1: 1D Cellular Automaton | ECE 5760 | | Lucas Ackerman (lba36)
Weiqing Li (wl336) | 9/16/2011 |

Introduction The purpose of this lab was to design a hardware implementation of an elementary cellular automaton and display the evolving state of that automaton on a VGA screen (with a minimum resolution of 320x240). In the implementation, there was to be a way for the user to set the evolution rule which defined how each new generation formed from the previous generation. Additionally, the user should be able to press a button and display another screen with the next set of evolved states. The initial condition was to be selectable and either a single white cell in the center of the first generation or a random cellular automaton. In the proceeding sections we will discuss our implementation, the results, comparisons with natural phenomena and possible improvements to the design.
Design and Testing
Overview
As mentioned above, the implementation of the 1D cellular automaton was completed in hardware and no software was designed. We used the Altera DE2 development board and the Cyclone II E2C35 FPGA. Since each cell of a new generation of the 1D cellular automaton is calculated by three cells from the previous generation and we wanted to generate the cellular automaton with hardware, a state machine running at 27Mhz was implemented to read from the memory, generate the new cell and store it back into memory. We also implemented a separate VGA interface that fed the VGA output with data and sync signals.
Memory Structure
All cellular automaton data was stored in M4K memory blocks because M4K block can be configured as dual-ported devices. This eliminates the need for synchronization between the state machine and the VGA interface since their memory accesses will not interfere with one another. On the other hand the read and writes to these M4K memory blocks are pipelined and require careful timing across clock cycles. However, in the end by choosing this implementation over using the on-board asynchronous SRAM we were able to achieve higher performance (i.e. new screens were redrawn more quickly).
New Cell Generation In the Wolfram-defined calculation rules for 1D cellular automaton, a parent generation (consisting of three individual cells from the previous generation) will produce a 1 or true value in the next generation if corresponding bit for that particular pattern is set in the rule. In other words, all possible combinations of the 3 parents in the 1 or 0 states are each mapped to one of the bits in the rule (an 8-bit value). Therefore, we can calculate the new child with the following simple combinational logic: assign child = ((8’b1<<parents) & rule) == 8’b0 ? 0 : 1; The statement above shows that in order to check if the corresponding bit is set in the rule for the given combination of parents we can perform a bit-wise AND between the rule and a logically shifted 1 (by the amount corresponding to the parents value) and check if the result is all zeroes or not. If at least one of the eight bits is 1 then we know that the new child should be set to 1, otherwise 0.
Initialization and Boundary Conditions
There are three kinds of initial conditions in our implementation. The first one is seed mode, which means the first line only has one cell active (value is 1) in the middle. The second is random mode, which means each cell of the first line takes a random value. The third is new-page mode, which takes the last line of the cellular automaton to the first line and starts drawing a new generation. The seed mode and random mode are controlled by a switch on the board. These modes are only updated when KEY0, the reset button, is pressed. Similarly, the new-page mode is activated only when KEY3 is pressed and the state machine is allowed to resume. Two boundary conditions are implemented. The first one assumed the cells at the two ends of the screen were always 0. The second assumed each line was actually circular so the last cell connects with the first cell and vice versa. The boundary conditions are controlled by single switch and are updated only at reset.
Color Mode
We also implemented a simple color scheme that is based on X and Y position on screen. To do this we directly used the coordinates that are supplied by the VGA controller (indicating which pixel is currently being drawn) as part of the R, G and B color vectors. By experimenting with different combinations of the X and Y coordinates we eventually settled on a pattern that produced color stripping across the screen in a vertical fashion. As with the features mentioned above, we tied the color enable to a switch which could be updated at reset.

RTL Block Diagram
27 MHz Clock
VGA Clock we Memory (M4K)
Cellular Automaton State Machine data address
VGA Interface we address data Audio PLL
Switches
Keys
To Monitor

The diagram above illustrates the high-level operation of our hardware design. At the center is the memory system, which as mentioned previously is realized with M4K blocks and is dual-ported. On the left we see that the main state machine takes the values of the switches and push-buttons as inputs and outputs the address and write-enable lines to one of the ports on the memory system. In addition there is a bi-directional data bus between the state machine and the memory. Similarly, the VGA interface writes to the address line on the other port of the memory system and reads from the data bus (the WE is always kept false). This VGA interface (via a VGA controller) converts the RGB data returned from memory into the proper format and outputs it to the VGA lines. To coordinate all of the timing between these three systems, an on-board Audio-PLL is used to create the appropriate clock signals for the VGA interface and the memory system.

State Machine
The state machine for creating the cellular automaton is represented in the diagram below:
KEY0 Pressed
Init 1

Init 2

Init 3

(KEY3 Press | Rand Mode) & !(end of 1st line)
Reset

Calc 1

Calc 2

Calc 4

Calc 3

Calc 6

Calc 5

WaitForKey

Last Pixel on Screen
!(KEY3 Pressed)
KEY3 Pressed Draw New Screen

Figure 1 – CA State Machine
Starting from the beginning, the state machine is fully initialized in the Reset state. In particular, during this state all of the memory is written with 0’s (black), the rule, color setting, boundary condition and random seed are updated. Next, the initialization portion is used to set the first line of the cellular automaton and is consisted of three states: Init, Init1 and Init2. Each of these three initialization stages is sub-divided depending on whether the user selected seed or random mode, or if the user pressed KEY3 (draw new screen). In all three of these modes however, Init1 is responsible for registering the data and address for the next write, Init2 actually enables the write, and Init3 either prepares to loop back to Init and begin another write or moves into the Calc1 state and prepares to draw the next generation.
Calculating the a new child consists of the six states shown above: Calc1 – Calc6. Since Init3 loads the address for reading the first parent, Calc1 will load the address for reading the second parent and Calc2 will load the address for reading the third. Since the reads are pipelined, the first parent will be read out and registered in Calc2, the second in Calc3 and the third in Calc4. Therefore in Calc5 the state machine calculates the new child and checks to see if we’ve reached a boundary (right or bottom edges). If the bottom edge of the screen hasn’t been reached we will move into Calc6 and initialize the read for the next parent, otherwise we move into the WaitForKey state.
In the WaitForKey state, the state machine is simply idle and waits for outside action by the user. If the user pressed KEY3 a flag will be set and the state machine will move back into the Init1 state and begin drawing a new screen. If the user instead presses the reset button, the state machine will be reset.
Testing (Luke)
One of our main methods for testing our design was using the LEDs on the FPGA board. For example, in our code we wired LEDR[17:14] to the state register so that as the state machine executed we could observe the current state. In perfect operation the state changes would happen too quickly to observe, and instead we would almost always see that the machine was sitting in the “WaitForKey” state described above. However, in the earlier stages of debugging it was useful to determine when the state machine was getting stuck in a particular state. Once the screen was drawing without tearing or other video artifacts we needed to verify that the cellular automaton was evolving correctly. To do this we used the UMass applet suggested in the lab, and selected the particular rule of interest and ran it under the initial conditions. Additionally, in order to troubleshoot problems with the evolution of an automaton as well as to verify the behavior at the boundaries (given a particular boundary condition), we developed a simple MATLAB script to draw the automaton for a given rule:

Figure 2 – CA Evolution Rule 30
For rule 30 (shown above) we can notice an interesting “folding-like” behavior at the left boundary which we similarly observed on the VGA screen. This was one way of confirming that the irregularity was in fact to be expected.

Results & Analysis
Sample Output
Below are a few images demonstrating how a CA initialized with a single cell in the center of the line evolved (spanning multiple 640 x 480 screens) for rule 30 and 90, which are somewhat representative patterns:

Figure 3 – CA Evolution Rule 30

Figure 4 – CA Evolution Rule 90 Looking at Figure 3 we see the same “folding” behavior at the boundary as we saw using the MATLAB screen. In fact, because of this boundary condition the artifact on the left edge slowly grows to consume the entire screen as seen in the last two images. However, because of our testing we were able to verify that this pattern is in fact correct. In contrast looking at Figure 4 we see that there is no observable artifact for rule 90 using the same boundary condition. The bottom image shows that the CA evolves as expected and simulated in MATLAB. Images for both rule 30 and 90 starting with a random seed are shown below:

Figure 5 – CA Evolution with Random Initialization - Rule 30 (Left) & 90 (Right)
In addition to these basic features, we implemented a coloring scheme that allowed the CA patterns to be drawn with multiple colors which could be selected by one of the DIP switches. The result for rule 30 is shown below:

Figure 6 – CA Evolution with color – Rule 30

Comparison with Natural Phenomena As discussed in class, many growing organisms in nature exhibit patterns similar to those produced by our hardware implementation of CAs. In particular, organisms that have “1-D like” growth tend to produce these types of patterns. One example referred to on the lab page is reproduced below:

Figure 7 – Cone Snail/Conus textile from Wikipedia.org (Left), Rule 30 (Right)

The image above shows a Cone Snail, which forms its shell by growing a thin layer of cells in a spiral fashion. We see clearly that this shell resembles the Rule 30 pattern displayed to the right. Another example is seen below:

Figure 8 – Parrot Fish from ethanhein.com (Left), Rule 54 (Right)
In this case we seen that the scales on the parrot fish very closely resemble the pattern produced by rule 54 on the right.
Conclusions
If more time had been available a few improvements could have been implemented. The color scheme used to change the cellular automaton from black and white to something more interesting could have been more representative of the natural analogs. Specifically, we could have chosen a color mapping algorithm that would have had smoother boundaries and included a larger portion of the color spectrum. Additionally, the artifacts introduced by our particular boundary condition made some patterns difficult to observe. To avoid this we could have allowed the user to select the boundary condition from the DIP switches so that we could tune this for each rule (i.e. always black, always white, circular, etc).
However, in the end our hardware implementation of 1D cellular automata was a success. Our system was capable of taking a user selected rule and drawing successive generations of the automaton indefinitely (given either a random or seed generation). By choosing to implement the memory with M4K blocks instead of the asynchronous SRAM, the time taken to draw a new screen of generations was almost imperceptible to the human eye. The system not only demonstrated a fundamental mathematical concept but illustrated the ability of computational systems to replicate some of nature’s basic life processes.

Documentation
Code
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// Cornell University - ECE 5760
// Luke Ackerman (lba36)& Weiqing Li (wl336)
// Lab 1: 1-dimensional Cellular Automaton (CA)
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// Code based on VGA_640x480_m4k_DLADLA Template:
// Bruce R Land, Cornell University, Oct 2009
// Improved top module written by Adam Shapiro Oct 2009
// --------------------------------------------------------------------
// --------------------------------------------------------------------

module VGA_640x480_m4k_CA ( // Clock Input input CLOCK_27, // 27 MHz input CLOCK_50, // 50 MHz input EXT_CLOCK, // External Clock // Push Button input [3:0] KEY, // Pushbutton[3:0] // DPDT Switch input [17:0] SW, // Toggle Switch[17:0] // 7-SEG Display output [6:0] HEX0, // Seven Segment Digit 0 output [6:0] HEX1, // Seven Segment Digit 1 output [6:0] HEX2, // Seven Segment Digit 2 output [6:0] HEX3, // Seven Segment Digit 3 output [6:0] HEX4, // Seven Segment Digit 4 output [6:0] HEX5, // Seven Segment Digit 5 output [6:0] HEX6, // Seven Segment Digit 6 output [6:0] HEX7, // Seven Segment Digit 7 // LED output [8:0] LEDG, // LED Green[8:0] output [17:0] LEDR, // LED Red[17:0] // UART output UART_TXD, // UART Transmitter input UART_RXD, // UART Receiver // IRDA output IRDA_TXD, // IRDA Transmitter input IRDA_RXD, // IRDA Receiver // SDRAM Interface inout [15:0] DRAM_DQ, // SDRAM Data bus 16 Bits output [11:0] DRAM_ADDR, // SDRAM Address bus 12 Bits output DRAM_LDQM, // SDRAM Low-byte Data Mask output DRAM_UDQM, // SDRAM High-byte Data Mask output DRAM_WE_N, // SDRAM Write Enable output DRAM_CAS_N, // SDRAM Column Address Strobe output DRAM_RAS_N, // SDRAM Row Address Strobe output DRAM_CS_N, // SDRAM Chip Select output DRAM_BA_0, // SDRAM Bank Address 0 output DRAM_BA_1, // SDRAM Bank Address 0 output DRAM_CLK, // SDRAM Clock output DRAM_CKE, // SDRAM Clock Enable // Flash Interface inout [7:0] FL_DQ, // FLASH Data bus 8 Bits output [21:0] FL_ADDR, // FLASH Address bus 22 Bits output FL_WE_N, // FLASH Write Enable output FL_RST_N, // FLASH Reset output FL_OE_N, // FLASH Output Enable output FL_CE_N, // FLASH Chip Enable // SRAM Interface inout [15:0] SRAM_DQ, // SRAM Data bus 16 Bits output [17:0] SRAM_ADDR, // SRAM Address bus 18 Bits output SRAM_UB_N, // SRAM High-byte Data Mask output SRAM_LB_N, // SRAM Low-byte Data Mask output SRAM_WE_N, // SRAM Write Enable output SRAM_CE_N, // SRAM Chip Enable output SRAM_OE_N, // SRAM Output Enable // ISP1362 Interface inout [15:0] OTG_DATA, // ISP1362 Data bus 16 Bits output [1:0] OTG_ADDR, // ISP1362 Address 2 Bits output OTG_CS_N, // ISP1362 Chip Select output OTG_RD_N, // ISP1362 Write output OTG_WR_N, // ISP1362 Read output OTG_RST_N, // ISP1362 Reset output OTG_FSPEED, // USB Full Speed, 0 = Enable, Z = Disable output OTG_LSPEED, // USB Low Speed, 0 = Enable, Z = Disable input OTG_INT0, // ISP1362 Interrupt 0 input OTG_INT1, // ISP1362 Interrupt 1 input OTG_DREQ0, // ISP1362 DMA Request 0 input OTG_DREQ1, // ISP1362 DMA Request 1 output OTG_DACK0_N, // ISP1362 DMA Acknowledge 0 output OTG_DACK1_N, // ISP1362 DMA Acknowledge 1 // LCD Module 16X2 inout [7:0] LCD_DATA, // LCD Data bus 8 bits output LCD_ON, // LCD Power ON/OFF output LCD_BLON, // LCD Back Light ON/OFF output LCD_RW, // LCD Read/Write Select, 0 = Write, 1 = Read output LCD_EN, // LCD Enable output LCD_RS, // LCD Command/Data Select, 0 = Command, 1 = Data // SD Card Interface inout SD_DAT, // SD Card Data inout SD_DAT3, // SD Card Data 3 inout SD_CMD, // SD Card Command Signal output SD_CLK, // SD Card Clock // I2C inout I2C_SDAT, // I2C Data output I2C_SCLK, // I2C Clock // PS2 input PS2_DAT, // PS2 Data input PS2_CLK, // PS2 Clock // USB JTAG link input TDI, // CPLD -> FPGA (data in) input TCK, // CPLD -> FPGA (clk) input TCS, // CPLD -> FPGA (CS) output TDO, // FPGA -> CPLD (data out) // VGA output VGA_CLK, // VGA Clock output VGA_HS, // VGA H_SYNC output VGA_VS, // VGA V_SYNC output VGA_BLANK, // VGA BLANK output VGA_SYNC, // VGA SYNC output [9:0] VGA_R, // VGA Red[9:0] output [9:0] VGA_G, // VGA Green[9:0] output [9:0] VGA_B, // VGA Blue[9:0] // Ethernet Interface inout [15:0] ENET_DATA, // DM9000A DATA bus 16Bits output ENET_CMD, // DM9000A Command/Data Select, 0 = Command, 1 = Data output ENET_CS_N, // DM9000A Chip Select output ENET_WR_N, // DM9000A Write output ENET_RD_N, // DM9000A Read output ENET_RST_N, // DM9000A Reset input ENET_INT, // DM9000A Interrupt output ENET_CLK, // DM9000A Clock 25 MHz // Audio CODEC inout AUD_ADCLRCK, // Audio CODEC ADC LR Clock input AUD_ADCDAT, // Audio CODEC ADC Data inout AUD_DACLRCK, // Audio CODEC DAC LR Clock output AUD_DACDAT, // Audio CODEC DAC Data inout AUD_BCLK, // Audio CODEC Bit-Stream Clock output AUD_XCK, // Audio CODEC Chip Clock // TV Decoder input [7:0] TD_DATA, // TV Decoder Data bus 8 bits input TD_HS, // TV Decoder H_SYNC input TD_VS, // TV Decoder V_SYNC output TD_RESET, // TV Decoder Reset // GPIO inout [35:0] GPIO_0, // GPIO Connection 0 inout [35:0] GPIO_1 // GPIO Connection 1
);

//Turn off all displays. assign HEX0 = 7'h7F; assign HEX1 = 7'h7F; assign HEX2 = 7'h7F; assign HEX3 = 7'h7F; assign HEX4 = 7'h7F; assign HEX5 = 7'h7F; assign HEX6 = 7'h7F; assign HEX7 = 7'h7F; //assign LEDR = 18'h0; // assign LEDG = 9'h0; //Set all GPIO to tri-state. assign GPIO_0 = 36'hzzzzzzzzz; assign GPIO_1 = 36'hzzzzzzzzz;

//Disable audio codec. //assign AUD_DACDAT = 1'b0; //assign AUD_XCK = 1'b0;

//Disable DRAM. assign DRAM_ADDR = 12'h0; assign DRAM_BA_0 = 1'b0; assign DRAM_BA_1 = 1'b0; assign DRAM_CAS_N = 1'b1; assign DRAM_CKE = 1'b0; assign DRAM_CLK = 1'b0; assign DRAM_CS_N = 1'b1; assign DRAM_DQ = 16'hzzzz; assign DRAM_LDQM = 1'b0; assign DRAM_RAS_N = 1'b1; assign DRAM_UDQM = 1'b0; assign DRAM_WE_N = 1'b1;

//Disable Ethernet. assign ENET_CLK = 1'b0; assign ENET_CS_N = 1'b1; assign ENET_CMD = 1'b0; assign ENET_DATA = 16'hzzzz; assign ENET_RD_N = 1'b1; assign ENET_RST_N = 1'b1; assign ENET_WR_N = 1'b1;

//Disable flash. assign FL_ADDR = 22'h0; assign FL_CE_N = 1'b1; assign FL_DQ = 8'hzz; assign FL_OE_N = 1'b1; assign FL_RST_N = 1'b1; assign FL_WE_N = 1'b1;

//Disable LCD. assign LCD_BLON = 1'b0; assign LCD_DATA = 8'hzz; assign LCD_EN = 1'b0; assign LCD_ON = 1'b0; assign LCD_RS = 1'b0; assign LCD_RW = 1'b0;

//Disable OTG. assign OTG_ADDR = 2'h0; assign OTG_CS_N = 1'b1; assign OTG_DACK0_N = 1'b1; assign OTG_DACK1_N = 1'b1; assign OTG_FSPEED = 1'b1; assign OTG_DATA = 16'hzzzz; assign OTG_LSPEED = 1'b1; assign OTG_RD_N = 1'b1; assign OTG_RST_N = 1'b1; assign OTG_WR_N = 1'b1;

//Disable SDRAM. assign SD_DAT = 1'bz; assign SD_CLK = 1'b0;

//Disable SRAM. assign SRAM_ADDR = 18'h0; assign SRAM_CE_N = 1'b1; assign SRAM_DQ = 16'hzzzz; assign SRAM_LB_N = 1'b1; assign SRAM_OE_N = 1'b1; assign SRAM_UB_N = 1'b1; assign SRAM_WE_N = 1'b1;

//Disable all other peripherals. //assign I2C_SCLK = 1'b0; assign IRDA_TXD = 1'b0; //assign TD_RESET = 1'b0; assign TDO = 1'b0; assign UART_TXD = 1'b0;

wire VGA_CTRL_CLK; wire AUD_CTRL_CLK; wire DLY_RST;

assign TD_RESET = 1'b1; // Allow 27 MHz assign AUD_ADCLRCK = AUD_DACLRCK; assign AUD_XCK = AUD_CTRL_CLK;

Reset_Delay r0 ( .iCLK(CLOCK_50),.oRESET(DLY_RST) );

VGA_Audio_PLL p1 ( .areset(~DLY_RST),.inclk0(CLOCK_27),.c0(VGA_CTRL_CLK),.c1(AUD_CTRL_CLK),.c2(VGA_CLK) );

VGA_Controller u1 ( // Host Side .iCursor_RGB_EN(4'b0111), .oAddress(mVGA_ADDR), .oCoord_X(Coord_X), .oCoord_Y(Coord_Y), .iRed(mVGA_R), .iGreen(mVGA_G), .iBlue(mVGA_B), // VGA Side .oVGA_R(VGA_R), .oVGA_G(VGA_G), .oVGA_B(VGA_B), .oVGA_H_SYNC(VGA_HS), .oVGA_V_SYNC(VGA_VS), .oVGA_SYNC(VGA_SYNC), .oVGA_BLANK(VGA_BLANK), // Control Signal .iCLK(VGA_CTRL_CLK), .iRST_N(DLY_RST) );

wire [9:0] mVGA_R; //memory output to VGA wire [9:0] mVGA_G; wire [9:0] mVGA_B; wire [18:0] mVGA_ADDR; //video memory address wire [9:0] Coord_X, Coord_Y; //display coods

////////////////////////////////////
//CA state machine variables wire reset; //tied to a button for reseting the state-machine reg [3:0] state; //state machine reg [30:0] x_rand; //shift registers for random number gen reg [30:0] x_seed; //seed for the random number shift register wire x_low_bit;//rand low bits for SR reg init_mode; //defines the first generation of the CA: seed or random (only updated at reset) reg [7:0] rule; //next generation calculation rule (only updated at reset) reg [2:0] parents; //stores the 3 cells from the previous generation that create the child reg [9:0] curr_X; //current x position in creating a new image reg [8:0] curr_Y; //current y position in creating a new image reg k3press; //used for getting out of wait state when key[3] is pressed wire child; //stores the value of the new cell to be written to memory assign child = ((rule & (8'b1 << parents)) != 8'b0) ? 1'b1 : 1'b0; //calculate new child cell value reg [640:0] lastGen; //stores the last generation created on the screen for redrawing reg colorON; //controls whether the drawn image is white-only or color (only updated at reset)

// Show m4k on the VGA
// -- use m4k a for state machine
// -- use m4k b for VGA refresh wire mem_bit ; //current data from m4k to VGA reg disp_bit ; // registered data from m4k to VGA wire state_bit ; // current data from m4k to state machine reg we ; // write enable for a reg [18:0] addr_reg ; // for a reg data_reg ; // for a vga_buffer display( .address_a (addr_reg), .address_b ({Coord_X[9:0],Coord_Y[8:0]}), // vga current address .clock_a (VGA_CTRL_CLK), .clock_b (VGA_CTRL_CLK), .data_a (data_reg), .data_b (1'b0), // never write on port b .wren_a (we), .wren_b (1'b0), // never write on port b .q_a (state_bit), .q_b (mem_bit) ); // data used to update VGA

//draw with color or white depending on the state of a switch at reset assign mVGA_R = colorON ? {10{disp_bit}} & {{Coord_Y[8:3]},{4{disp_bit}}} : {10{disp_bit}}; assign mVGA_G = colorON ? {10{disp_bit}} & {{Coord_X[9:4]},{4{disp_bit}}} : {10{disp_bit}}; assign mVGA_B = colorON ? {10{disp_bit}} & {{Coord_X[9:6],Coord_Y[8:5]},{4{disp_bit}}} : {10{disp_bit}};

// CA state machine reset tied to KEY 0 assign reset = ~KEY[0];

//right-most bit for rand number shift regs
//your basic XOR random # gen assign x_low_bit = x_rand[27] ^ x_rand[30];

// Use LEDs for status assign LEDR[17:14] = state; //display the state on the LEDs assign LEDG[0] = reset; //tie reset button to LED assign LEDG[7] = ~KEY[3]; //tie new screen button to LED

// Screen boundaries parameter xmin=10'd8, xmax=10'd631, ymin=9'd4, ymax=9'd472, xcenter=10'd310;

// State names parameter init=4'd0, init1=4'd1, init2=4'd2, cell_calc1=4'd3, cell_calc2=4'd4, cell_calc3=4'd5, cell_calc4=4'd6, cell_calc5=4'd7, cell_calc6=4'd8, waitForKey=4'd9; // Read memory out to VGA display always @ (negedge VGA_CTRL_CLK) begin // register the m4k output for better timing on VGA // negedge seems to work better than posedge disp_bit <= mem_bit; end //State Machine Control always @ (posedge VGA_CTRL_CLK) //VGA_CTRL_CLK begin x_seed <= x_seed + 1; //update the seed variable on every clock cycle // register the m4k output for better timing on VGA if (reset) //synch reset assumes KEY0 is held down 1/60 second begin //clear the screen addr_reg <= {Coord_X[9:0],Coord_Y[8:0]} ; // [17:0] we <= 1'b1; //write some memory data_reg <= 1'b0; //write all zeros (black) //init random number generators to alternating bits x_rand <= x_seed; init_mode <= SW[17]; //store the user-selected initial state for the CA (0=seed, 1=random) rule <= SW[7:0]; //store the user-selected CA generation calculation rule (0-255) curr_X <= xmin; //ignore first cell in each generation (boundary condition) curr_Y <= ymin + 9'd1; //draw new generation on the 2nd line k3press <= 1'b0; //clear flag for key 3 press lastGen <= 633'd0; //clear the last generation register (for redrawing) colorON <= SW[16]; //store user choice for drawing CA in color state <= init; //first state in regular state machine end //begin state machine to modify display else if (KEY[3]) // KEY3 is next screen button (ACTIVE LOW) begin case(state) init: //register addr,data for writing begin we <= 1'b0 ; // disable writing if (k3press) begin //draw last line from prev. screen addr_reg <= {curr_X, curr_Y}; data_reg <= lastGen[curr_X]; //load next bit of last line state <= init1; end else if (init_mode) begin //random mode addr_reg <= {curr_X, ymin}; data_reg <= x_rand[30]; //load new random bit state <= init1; end else if (~init_mode) begin //seed mode //write a white dot in the middle of the first line addr_reg <= {xcenter,ymin}; //(x,y) data_reg <= 1'b1; //white state <= init1; end end init1: //delay enable 'we' to account for registering addr,data begin if (k3press) begin //draw last line from prev. screen we <= 1'b1; //enable writing data_reg <= lastGen[curr_X]; //load next bit of last line state <= init2; end else if (init_mode) begin //random mode we <= 1'b1; //enable writing data_reg <= x_rand[30]; //load new random bit state <= init2; end else if (~init_mode) begin //seed mode we <= 1'b1; //enable writing data_reg <= 1'b1; state <= init2; end end init2: //finish writing begin if (k3press) begin //draw last line from prev. screen if (curr_X <= xmax) begin we <= 1'b1; state <= init; curr_X <= curr_X + 10'd1; //update position end else begin k3press <= 1'b0; we <= 1'b0; addr_reg <= {xmin-10'd1,ymin}; //read parent 2 curr_X <= xmin; curr_Y <= ymin+9'd1; state <= cell_calc1; end end else if (init_mode) begin //random mode if (curr_X <= xmax) begin we <= 1'b1; state <= init; curr_X <= curr_X + 10'd1; //update position x_rand <= {x_rand[29:0], x_low_bit}; //update random number end else begin we <= 1'b0; addr_reg <= {xmin-10'd1,ymin}; //read parent 2 curr_X <= xmin; curr_Y <= ymin+9'd1; state <= cell_calc1; end end else if (~init_mode) begin //seed mode // use result TWO cycles later // -- one to load the addr reg, one to read memory we <= 1'b0; addr_reg <= {curr_X-10'd1,curr_Y-9'd1}; //read parent 2 state <= cell_calc1; end end cell_calc1: //start reading the 2nd parent begin we <= 1'b0; //no memory write addr_reg <= {curr_X,curr_Y-9'd1}; //read parent 1 state <= cell_calc2 ; end cell_calc2: //store the first parent, start reading the 3rd begin we <= 1'b0; //no memory write parents[2] <= state_bit; //store parent 2 addr_reg <= {curr_X+10'd1,curr_Y-9'd1}; //read parent 0 state <= cell_calc3 ; end cell_calc3: //store the 2nd parent begin we <= 1'b0; //no memory write parents[1] <= state_bit; //store parent 1 state <= cell_calc4 ; end cell_calc4: //store the 3rd parent begin we <= 1'b0; //no memory write parents[0] <= state_bit; //store parent 0 addr_reg <= {curr_X,curr_Y}; //new child address state <= cell_calc5 ; end cell_calc5: //write the new child to memory and check the boundary begin we <= 1'b1; //write child data_reg <= child; //store the last line of pixels for redrawing on the next screen if (curr_Y == ymax) lastGen[curr_X] <= child; else lastGen <= lastGen; //check if we've reached a screen boundary if (curr_X < xmax) begin curr_X <= curr_X + 10'b1; state <= cell_calc6; end else if (curr_Y < ymax) begin curr_Y <= curr_Y + 9'b1; curr_X <= xmin; state <= cell_calc6; end else begin k3press <= 1'b0; state <= waitForKey; end end cell_calc6: //starting reading new 1st parent begin we <= 1'b0; //disable writing addr_reg <= {curr_X-10'd1,curr_Y-9'd1}; //read new parent 2 state <= cell_calc1; end waitForKey: //wait until the user presses KEY3 to redraw the screen begin if (k3press) begin state <= init; we <= 1'b1; end else begin state <= waitForKey; we <= 1'b0; end end endcase end // else if (KEY[3]) else // User presses KEY3 and no reset begin if ((curr_X == xmax) && (curr_Y == ymax) && (state == waitForKey)) begin k3press <= 1'b1; //set flag to re-initialize state machine curr_X <= xmin; //reset CA position curr_Y <= ymin; end end end // always @ (posedge VGA_CTRL_CLK)

endmodule //top module

////////// end of file //////////////////////////

Similar Documents