├── LICENSE ├── README.md ├── compile.py ├── fpga ├── whack.qpf └── whack.qsf ├── pixel_alu.v ├── pixel_fifo.v ├── pixel_processor.v ├── sram_1r1w.v ├── testbench.v ├── top.v └── vga_timing_generator.v /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The inspiration for this is an a sample application from BeOS: 2 | 3 | http://www.haiku-os.org/legacy-docs/benewsletter/Issue3-51.html#Engineering3-51-2 4 | 5 | To use: 6 | 7 | python compile.py 8 | 9 | _enter a formula followed by ctrl-D_ 10 | 11 | x+y+f 12 | 13 | Synthesize the design on Quartus. This has been tested on the Cyclone II starter kit dev board. 14 | 15 | Details of operation are in the Wiki: https://github.com/jbush001/FPGAWhack/wiki 16 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2013 Jeff Bush 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | import sys 18 | 19 | class CodeGenerator: 20 | def __init__(self, filename): 21 | self.operandStack = [] 22 | self.freeRegisters = [ x for x in range(3, -1, -1) ] 23 | self.outputFile = open(filename, 'w') 24 | self.numInstructions = 0 25 | 26 | def pushConstant(self, value): 27 | self.operandStack += [ ('const', value) ] 28 | 29 | def pushVariableRef(self, index): 30 | self.operandStack += [ ('freg', index ) ] 31 | 32 | def doOp(self, operation): 33 | type2, op2 = self.operandStack.pop() 34 | type1, op1 = self.operandStack.pop() 35 | if type1 == 'const': 36 | # If the first operator is a constant, copy it into a register 37 | tmpReg = self._allocateTemporary() 38 | self._emitInstruction(8, tmpReg, 0, 0, 1, op1) 39 | type1 = 'reg' 40 | op1 = tmpReg 41 | 42 | # Free up temporary registers, allocate a result reg 43 | if type2 == 'reg': self._freeTemporary(op2) 44 | if type1 == 'reg': self._freeTemporary(op1) 45 | resultReg = self._allocateTemporary() 46 | self.operandStack += [ ('reg', resultReg)] 47 | if type2 == 'const': 48 | self._emitInstruction(operation, resultReg, op1, 0, 1, op2) 49 | else: 50 | self._emitInstruction(operation, resultReg, op1, op2, 0, 0) 51 | 52 | def saveResult(self): 53 | # Emit an instruction to move into the result register (7) 54 | type, op = self.operandStack.pop() 55 | if type == 'reg' or type == 'freg': 56 | self._emitInstruction(0, 7, op, op, 0, 0) 57 | elif type == 'const': 58 | self._emitInstruction(8, 7, 0, 0, 1, op) # Constant 59 | else: 60 | raise Exception('internal error: bad type on operand stack') 61 | 62 | # Pad the remaining instructions with NOPs. 63 | for i in range(self.numInstructions, 16): 64 | self.outputFile.write('0000000000000\n') 65 | 66 | self.outputFile.close() 67 | 68 | def _emitInstruction(self, opcode, dest, srca, srcb, isConst, constVal): 69 | self.numInstructions += 1 70 | if self.numInstructions == 16: 71 | raise Exception('formula too complex: exceeded instruction memory') 72 | 73 | self.outputFile.write('%013x\n' % ((dest << 43) | (srca << 40) | (srcb << 37) | (opcode << 33) | (isConst << 32) | constVal)) 74 | 75 | # Pretty print operation 76 | pretty = [ 'and', 'xor', 'or', 'add', 'sub', 'mul', 'shl', 'shr', 'mov', 77 | 'eq', 'neq', 'gt', 'gte', 'lt', 'lte' ] 78 | if isConst: 79 | print '%s r%d, r%d, #%d' % (pretty[opcode], dest, srca, constVal) 80 | else: 81 | print '%s r%d, r%d, r%d' % (pretty[opcode], dest, srca, srcb) 82 | 83 | def _allocateTemporary(self): 84 | if len(self.freeRegisters) == 0: 85 | raise Exception('formula too complex: out of registers') 86 | else: 87 | return self.freeRegisters.pop() 88 | 89 | def _freeTemporary(self, val): 90 | self.freeRegisters += [ val ] 91 | 92 | class Scanner: 93 | def __init__(self, stream): 94 | self.pushBackChar = None 95 | self.pushBackToken = False 96 | self.lastToken = None 97 | self.stream = stream 98 | 99 | def pushBack(self): 100 | self.pushBackToken = True 101 | 102 | MULTIBYTE_TOKENS = { 103 | '>' : [ '=', '>' ], 104 | '<' : [ '=', '<' ], 105 | '=' : [ '=' ], 106 | '!' : [ '=' ], 107 | } 108 | 109 | def nextToken(self): 110 | if self.pushBackToken: 111 | self.pushBackToken = False 112 | return self.lastToken 113 | 114 | # Consume whitespace 115 | ch = self._nextChar() 116 | while ch.isspace(): 117 | ch = self._nextChar() 118 | 119 | # Get next token 120 | if ch == '': 121 | # End of string 122 | self.lastToken = None 123 | elif ch.isdigit(): 124 | lookahead = self._nextChar() 125 | if lookahead == '': 126 | self.lastToken = None 127 | elif ch == '0' and lookahead == 'x': 128 | # Parse a hexadecimal number 129 | number = 0 130 | while True: 131 | ch = self._nextChar() 132 | if ch >= 'A' and ch <= 'F': 133 | number = (number * 16) + (ord(ch) - ord('A') + 10) 134 | elif ch >= 'a' and ch <= 'f': 135 | number = (number * 16) + (ord(ch) - ord('a') + 10) 136 | elif ch.isdigit(): 137 | number = (number * 16) + (ord(ch) - ord('0')) 138 | else: 139 | self._pushBackChar(ch) 140 | break 141 | 142 | self.lastToken = number 143 | else: 144 | # Parse a decimal number 145 | self._pushBackChar(lookahead) 146 | number = 0 147 | while ch.isdigit(): 148 | number = (number * 10) + (ord(ch) - ord('0')) 149 | ch = self._nextChar() 150 | 151 | self._pushBackChar(ch) 152 | self.lastToken = number 153 | elif ch in self.MULTIBYTE_TOKENS: 154 | secondchars = self.MULTIBYTE_TOKENS[ch] 155 | lookahead = self._nextChar() 156 | if lookahead in secondchars: 157 | self.lastToken = ch + lookahead 158 | else: 159 | self._pushBackChar(lookahead) 160 | self.lastToken = ch 161 | elif ch.isalpha(): 162 | strval = ch 163 | while True: 164 | ch = self._nextChar() 165 | if ch.isalnum(): 166 | strval += ch 167 | else: 168 | self._pushBackChar(ch) 169 | break 170 | 171 | self.lastToken = strval 172 | else: 173 | # Single character symbolic token 174 | self.lastToken = ch 175 | 176 | return self.lastToken 177 | 178 | def _nextChar(self): 179 | if self.pushBackChar: 180 | ch = self.pushBackChar 181 | self.pushBackChar = None 182 | return ch 183 | else: 184 | return self.stream.read(1) 185 | 186 | def _pushBackChar(self, ch): 187 | self.pushBackChar = ch 188 | 189 | class Parser: 190 | def __init__(self, inputStream, generator): 191 | self.scanner = Scanner(inputStream) 192 | self.generator = generator 193 | 194 | def parse(self): 195 | self._parseExpression() 196 | self.generator.saveResult() 197 | 198 | def _parseExpression(self): 199 | self._parseUnaryExpression() 200 | self._parseInfixExpression(0) 201 | 202 | BUILTIN_VARS = { 203 | 'x' : 4, 204 | 'ix' : 4, 205 | 'y' : 5, 206 | 'iy' : 5, 207 | 'f' : 6 208 | } 209 | 210 | def _parsePrimaryExpression(self): 211 | tok = self.scanner.nextToken() 212 | if tok == '(': 213 | self._parseExpression() 214 | tok = self.scanner.nextToken() 215 | if tok != ')': 216 | raise Exception('parse error: expected )') 217 | elif isinstance(tok, int) or isinstance(tok, long): 218 | self.generator.pushConstant(tok) 219 | elif tok in self.BUILTIN_VARS: 220 | self.generator.pushVariableRef(self.BUILTIN_VARS[tok]) 221 | else: 222 | raise Exception('unexpected: ' + str(tok)) 223 | 224 | def _parseUnaryExpression(self): 225 | lookahead = self.scanner.nextToken() 226 | if lookahead == '-': 227 | self.generator.pushConstant(0) 228 | self._parseUnaryExpression() 229 | self.generator.doOp(4) # Subtract 230 | elif lookahead == '~': 231 | self._parseUnaryExpression() 232 | self.generator.pushConstant(0xffffffff) 233 | self.generator.doOp(1) # Exclusive Or 234 | elif lookahead == '!': 235 | self._parseUnaryExpression() 236 | self.generator.pushConstant(0) 237 | self.generator.doOp(9) # Equal to 238 | else: 239 | self.scanner.pushBack() 240 | self._parsePrimaryExpression() 241 | 242 | # Operator lookup table 243 | # (precedence, opcode) 244 | OPERATORS = { 245 | '|' : ( 1, 2 ), 246 | '^' : ( 2, 1 ), 247 | '&' : ( 3, 0 ), 248 | '==' : ( 4, 9 ), 249 | '!=' : ( 4, 10 ), 250 | '>' : ( 5, 11 ), 251 | '<' : ( 5, 13 ), 252 | '>=' : ( 5, 12 ), 253 | '<=' : ( 5, 14 ), 254 | '<<' : ( 6, 6 ), 255 | '>>' : ( 6, 7 ), 256 | '+' : ( 7, 3 ), 257 | '-' : ( 7, 4 ), 258 | '*' : ( 8, 5 ), 259 | # '/' : ( 9, -1 ) 260 | } 261 | 262 | # https://en.wikipedia.org/wiki/Operator-precedence_parser#Precedence_climbing_method 263 | def _parseInfixExpression(self, minPrecedence): 264 | while True: 265 | outerOp = self.scanner.nextToken() 266 | if outerOp not in self.OPERATORS: 267 | self.scanner.pushBack() 268 | break 269 | 270 | outerPrecedence, outerOpcode = self.OPERATORS[outerOp] 271 | if outerPrecedence < minPrecedence: 272 | self.scanner.pushBack() 273 | break 274 | 275 | self._parseUnaryExpression() 276 | while True: 277 | lookahead = self.scanner.nextToken() 278 | self.scanner.pushBack() 279 | if lookahead not in self.OPERATORS: 280 | break 281 | 282 | innerPrecedence, _ignore = self.OPERATORS[lookahead] 283 | if innerPrecedence <= outerPrecedence: 284 | break 285 | 286 | self._parseInfixExpression(innerPrecedence) 287 | 288 | self.generator.doOp(outerOpcode) 289 | 290 | p = Parser(sys.stdin, CodeGenerator('microcode.hex')) 291 | p.parse() 292 | 293 | -------------------------------------------------------------------------------- /fpga/whack.qpf: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # 3 | # Copyright (C) 1991-2011 Altera Corporation 4 | # Your use of Altera Corporation's design tools, logic functions 5 | # and other software and tools, and its AMPP partner logic 6 | # functions, and any output files from any of the foregoing 7 | # (including device programming or simulation files), and any 8 | # associated documentation or information are expressly subject 9 | # to the terms and conditions of the Altera Program License 10 | # Subscription Agreement, Altera MegaCore Function License 11 | # Agreement, or other applicable license agreement, including, 12 | # without limitation, that your use is for the sole purpose of 13 | # programming logic devices manufactured by Altera and sold by 14 | # Altera or its authorized distributors. Please refer to the 15 | # applicable agreement for further details. 16 | # 17 | # -------------------------------------------------------------------------- # 18 | # 19 | # Quartus II 20 | # Version 11.0 Build 208 07/03/2011 Service Pack 1 SJ Web Edition 21 | # Date created = 08:27:08 April 09, 2013 22 | # 23 | # -------------------------------------------------------------------------- # 24 | 25 | QUARTUS_VERSION = "11.0" 26 | DATE = "08:27:08 April 09, 2013" 27 | 28 | # Revisions 29 | 30 | PROJECT_REVISION = "whack" 31 | -------------------------------------------------------------------------------- /fpga/whack.qsf: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------- # 2 | # 3 | # Copyright (C) 1991-2011 Altera Corporation 4 | # Your use of Altera Corporation's design tools, logic functions 5 | # and other software and tools, and its AMPP partner logic 6 | # functions, and any output files from any of the foregoing 7 | # (including device programming or simulation files), and any 8 | # associated documentation or information are expressly subject 9 | # to the terms and conditions of the Altera Program License 10 | # Subscription Agreement, Altera MegaCore Function License 11 | # Agreement, or other applicable license agreement, including, 12 | # without limitation, that your use is for the sole purpose of 13 | # programming logic devices manufactured by Altera and sold by 14 | # Altera or its authorized distributors. Please refer to the 15 | # applicable agreement for further details. 16 | # 17 | # -------------------------------------------------------------------------- # 18 | # 19 | # Quartus II 20 | # Version 11.0 Build 208 07/03/2011 Service Pack 1 SJ Web Edition 21 | # Date created = 08:27:08 April 09, 2013 22 | # 23 | # -------------------------------------------------------------------------- # 24 | # 25 | # Notes: 26 | # 27 | # 1) The default values for assignments are stored in the file: 28 | # whack_assignment_defaults.qdf 29 | # If this file doesn't exist, see file: 30 | # assignment_defaults.qdf 31 | # 32 | # 2) Altera recommends that you do not modify this file. This 33 | # file is updated automatically by the Quartus II software 34 | # and any changes you make may be lost or overwritten. 35 | # 36 | # -------------------------------------------------------------------------- # 37 | 38 | 39 | set_global_assignment -name FAMILY "Cyclone II" 40 | set_global_assignment -name DEVICE EP2C20F484C7 41 | set_global_assignment -name TOP_LEVEL_ENTITY top 42 | set_global_assignment -name ORIGINAL_QUARTUS_VERSION "11.0 SP1" 43 | set_global_assignment -name PROJECT_CREATION_TIME_DATE "08:27:08 APRIL 09, 2013" 44 | set_global_assignment -name LAST_QUARTUS_VERSION "11.0 SP1" 45 | set_location_assignment PIN_B10 -to blue_o[3] 46 | set_location_assignment PIN_A10 -to blue_o[2] 47 | set_location_assignment PIN_D11 -to blue_o[1] 48 | set_location_assignment PIN_A9 -to blue_o[0] 49 | set_location_assignment PIN_A8 -to green_o[3] 50 | set_location_assignment PIN_B9 -to green_o[2] 51 | set_location_assignment PIN_C10 -to green_o[1] 52 | set_location_assignment PIN_B8 -to green_o[0] 53 | set_location_assignment PIN_A11 -to hsync_o 54 | set_location_assignment PIN_B7 -to red_o[3] 55 | set_location_assignment PIN_A7 -to red_o[2] 56 | set_location_assignment PIN_C9 -to red_o[1] 57 | set_location_assignment PIN_D9 -to red_o[0] 58 | set_location_assignment PIN_B11 -to vsync_o 59 | set_location_assignment PIN_L1 -to clk 60 | set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 61 | set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 62 | set_global_assignment -name ERROR_CHECK_FREQUENCY_DIVISOR 1 63 | set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top 64 | set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top 65 | set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top 66 | 67 | 68 | 69 | 70 | set_global_assignment -name VERILOG_FILE ../sram_1r1w.v 71 | set_global_assignment -name VERILOG_FILE ../top.v 72 | set_global_assignment -name VERILOG_FILE ../pixel_processor.v 73 | set_global_assignment -name VERILOG_FILE ../pixel_fifo.v 74 | set_global_assignment -name VERILOG_FILE ../pixel_alu.v 75 | set_global_assignment -name VERILOG_FILE ../vga_timing_generator.v 76 | set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top -------------------------------------------------------------------------------- /pixel_alu.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | // 19 | // Contains register file and ALU to compute value for a single pixel 20 | // 21 | 22 | module pixel_alu( 23 | input clk, 24 | input[45:0] instruction, 25 | input[31:0] x_coord, 26 | input[31:0] y_coord, 27 | input[31:0] f_number, 28 | output reg[11:0] output_value); 29 | 30 | localparam OP_AND = 0; 31 | localparam OP_XOR = 1; 32 | localparam OP_OR = 2; 33 | localparam OP_ADD = 3; 34 | localparam OP_SUB = 4; 35 | localparam OP_MUL = 5; 36 | localparam OP_SHL = 6; 37 | localparam OP_SHR = 7; 38 | localparam OP_MOV = 8; 39 | localparam OP_EQ = 9; 40 | localparam OP_NEQ = 10; 41 | localparam OP_GT = 11; 42 | localparam OP_GTE = 12; 43 | localparam OP_LT = 13; 44 | localparam OP_LTE = 14; 45 | 46 | localparam REG_X = 4'd4; 47 | localparam REG_Y = 4'd5; 48 | localparam REG_F = 4'd6; 49 | localparam REG_RESULT = 4'd7; 50 | 51 | reg[31:0] registers[0:3]; 52 | 53 | wire[2:0] dest = instruction[45:43]; 54 | wire[2:0] srca = instruction[42:40]; 55 | wire[2:0] srcb = instruction[39:37]; 56 | wire[3:0] operation = instruction[36:33]; 57 | wire use_const = instruction[32]; 58 | wire[31:0] const_val = instruction[31:0]; 59 | reg[31:0] operand1; 60 | reg[31:0] operand2; 61 | reg[31:0] result; 62 | wire[31:0] difference; 63 | wire equal; 64 | wire less; 65 | integer i; 66 | 67 | initial 68 | begin 69 | output_value = 0; 70 | for (i = 0; i < 4; i = i + 1) 71 | registers[i] = 0; 72 | end 73 | 74 | always @* 75 | begin 76 | casez (srca) 77 | 3'b0??: operand1 = registers[srca[1:0]]; 78 | REG_X: operand1 = x_coord; 79 | REG_Y: operand1 = y_coord; 80 | REG_F: operand1 = f_number; 81 | default: operand1 = 32'dX; 82 | endcase 83 | 84 | if (use_const) 85 | operand2 = const_val; 86 | else 87 | begin 88 | casez (srcb) 89 | 3'b0??: operand2 = registers[srcb[1:0]]; 90 | REG_X: operand2 = x_coord; 91 | REG_Y: operand2 = y_coord; 92 | REG_F: operand2 = f_number; 93 | default: operand2 = 32'dX; 94 | endcase 95 | end 96 | end 97 | 98 | assign difference = operand1 - operand2; 99 | assign equal = difference == 0; 100 | assign less = difference[31]; // Difference is negative 101 | 102 | always @* 103 | begin 104 | case (operation) 105 | OP_AND: result = operand1 & operand2; 106 | OP_XOR: result = operand1 ^ operand2; 107 | OP_OR: result = operand1 | operand2; 108 | OP_ADD: result = operand1 + operand2; 109 | OP_SUB: result = difference; 110 | OP_MUL: result = operand1 * operand2; 111 | OP_SHL: result = operand1 << operand2; 112 | OP_SHR: result = operand1 >> operand2; 113 | OP_MOV: result = operand2; 114 | OP_EQ: result = equal; 115 | OP_NEQ: result = !equal; 116 | OP_GT: result = !equal && !less; 117 | OP_GTE: result = !less; 118 | OP_LT: result = less; 119 | OP_LTE: result = less || equal; 120 | default: result = 32'dX; 121 | endcase 122 | end 123 | 124 | always @(posedge clk) 125 | begin 126 | if (!dest[2]) 127 | registers[dest[1:0]] <= result; 128 | 129 | if (dest == REG_RESULT) 130 | output_value <= { result[23:20], result[15:12], result[7:4] }; 131 | end 132 | endmodule 133 | 134 | -------------------------------------------------------------------------------- /pixel_fifo.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | // 19 | // The pixel FIFO receives all values in parallel and shifts them out one at a time. 20 | // 21 | 22 | module pixel_fifo 23 | #(parameter NUM_PIXELS = 16, 24 | parameter PIXEL_WIDTH = 16, 25 | parameter VALUE_IN_WIDTH = NUM_PIXELS * PIXEL_WIDTH) 26 | 27 | (input clk, 28 | input reset, 29 | output reg almost_empty, 30 | output reg empty, 31 | input enqueue, 32 | input[VALUE_IN_WIDTH - 1:0] value_in, 33 | input dequeue, 34 | output[PIXEL_WIDTH - 1:0] value_out); 35 | 36 | localparam COUNT_WIDTH = $clog2(NUM_PIXELS) + 1; 37 | 38 | reg[PIXEL_WIDTH - 1:0] data[0:NUM_PIXELS - 1]; 39 | reg[COUNT_WIDTH - 1:0] element_count; 40 | assign value_out = data[0]; 41 | integer i; 42 | 43 | initial 44 | begin 45 | empty = 1; 46 | almost_empty = 1; 47 | element_count = 0; 48 | for (i = 0; i < NUM_PIXELS; i = i + 1) 49 | data[i] = 0; 50 | end 51 | 52 | always @(posedge clk, posedge reset) 53 | begin 54 | if (reset) 55 | begin 56 | element_count <= 0; 57 | almost_empty <= 0; 58 | empty <= 1; 59 | for (i = 0; i < NUM_PIXELS; i = i + 1) 60 | data[i] = 0; 61 | end 62 | else if (enqueue) 63 | begin 64 | element_count <= NUM_PIXELS; 65 | almost_empty <= 0; 66 | empty <= 0; 67 | for (i = 0; i < NUM_PIXELS; i = i + 1) 68 | data[i] <= value_in >> (i * PIXEL_WIDTH); 69 | end 70 | else if (dequeue) 71 | begin 72 | if (element_count == 1) 73 | begin 74 | empty <= 1; 75 | almost_empty <= 0; 76 | end 77 | else if (element_count == 2) 78 | almost_empty <= 1; 79 | 80 | if (element_count != 0) 81 | element_count <= element_count - 1; 82 | 83 | for (i = 0; i < NUM_PIXELS - 1; i = i + 1) 84 | data[i] <= data[i + 1]; 85 | end 86 | end 87 | endmodule 88 | -------------------------------------------------------------------------------- /pixel_processor.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | module pixel_processor 19 | #(parameter NUM_PIXELS = 8, 20 | parameter PIXEL_WIDTH = 12, 21 | parameter OUTPUT_WIDTH = NUM_PIXELS * PIXEL_WIDTH) 22 | 23 | (input clk, 24 | input new_frame, 25 | input start_next_batch, 26 | output[OUTPUT_WIDTH - 1:0] result, 27 | output result_ready); 28 | 29 | localparam INSTRUCTION_WIDTH = 46; 30 | 31 | reg[3:0] pc; 32 | wire[INSTRUCTION_WIDTH - 1:0] instruction; 33 | reg[31:0] y_coord; 34 | reg[31:0] f_number; 35 | 36 | genvar lane; 37 | generate 38 | for (lane = 0; lane < NUM_PIXELS; lane = lane + 1) 39 | begin : pixel_compute 40 | reg[31:0] x_coord = 0; 41 | 42 | always @(posedge clk) 43 | begin 44 | if (new_frame || (end_of_line && start_next_batch)) 45 | x_coord <= lane; 46 | else if (start_next_batch) 47 | x_coord <= x_coord + NUM_PIXELS; 48 | end 49 | 50 | pixel_alu pixel_alu0( 51 | .clk(clk), 52 | .instruction(instruction), 53 | .x_coord(x_coord), 54 | .y_coord(y_coord), 55 | .f_number(f_number), 56 | .output_value(result[lane * PIXEL_WIDTH+:PIXEL_WIDTH])); 57 | end 58 | endgenerate 59 | 60 | localparam MAX_INSTRUCTIONS = NUM_PIXELS * 2; 61 | 62 | sram_1r1w #(INSTRUCTION_WIDTH, MAX_INSTRUCTIONS) instruction_mem( 63 | .clk(clk), 64 | .rd_addr(pc), 65 | .rd_data(instruction), 66 | .wr_enable(0), 67 | .wr_addr(0), 68 | .wr_data({INSTRUCTION_WIDTH{1'b0}})); 69 | 70 | initial 71 | begin 72 | pc = 0; 73 | y_coord = 0; 74 | f_number = 0; 75 | end 76 | 77 | assign result_ready = pc == MAX_INSTRUCTIONS - 1; 78 | wire end_of_line = pixel_compute[0].x_coord == 640 - NUM_PIXELS; 79 | 80 | always @(posedge clk) 81 | begin 82 | if (new_frame || start_next_batch) 83 | pc <= 5'd0; 84 | else if (!result_ready) 85 | pc <= pc + 1; 86 | 87 | if (new_frame) 88 | begin 89 | f_number <= f_number + 1; 90 | y_coord <= 0; 91 | end 92 | else if (end_of_line && start_next_batch) 93 | y_coord <= y_coord + 1; 94 | end 95 | endmodule 96 | -------------------------------------------------------------------------------- /sram_1r1w.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | module sram_1r1w 19 | #(parameter DATA_WIDTH = 32, 20 | parameter SIZE = 1024, 21 | parameter ADDR_WIDTH = $clog2(SIZE)) 22 | 23 | (input clk, 24 | input [ADDR_WIDTH - 1:0] rd_addr, 25 | output reg[DATA_WIDTH - 1:0] rd_data = 0, 26 | input wr_enable, 27 | input [ADDR_WIDTH - 1:0] wr_addr, 28 | input [DATA_WIDTH - 1:0] wr_data); 29 | 30 | reg[DATA_WIDTH - 1:0] data[0:SIZE - 1]; 31 | integer i; 32 | 33 | initial 34 | begin 35 | for (i = 0; i < SIZE; i = i + 1) 36 | data[i] = 0; 37 | 38 | rd_data = 0; 39 | $readmemh("microcode.hex", data); 40 | end 41 | 42 | always @(posedge clk) 43 | begin 44 | if (wr_enable) 45 | data[wr_addr] <= wr_data; 46 | 47 | if (wr_addr == rd_addr && wr_enable) 48 | rd_data <= wr_data; 49 | else 50 | rd_data <= data[rd_addr]; 51 | end 52 | endmodule 53 | -------------------------------------------------------------------------------- /testbench.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | module testbench; 18 | integer i; 19 | 20 | reg clk; 21 | wire vsync; 22 | wire hsync; 23 | wire[3:0] red; 24 | wire[3:0] blue; 25 | wire[3:0] green; 26 | 27 | top top( 28 | .clk(clk), 29 | .vsync_o(vsync), 30 | .hsync_o(hsync), 31 | .red_o(red), 32 | .blue_o(blue), 33 | .green_o(green)); 34 | 35 | initial 36 | begin 37 | $dumpfile("trace.lxt"); 38 | $dumpvars; 39 | 40 | clk = 0; 41 | for (i = 0; i < 400000; i = i + 1) 42 | #5 clk = !clk; 43 | end 44 | endmodule 45 | -------------------------------------------------------------------------------- /top.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | module top( 18 | input clk, 19 | output vsync_o, 20 | output hsync_o, 21 | output [3:0] red_o, 22 | output [3:0] blue_o, 23 | output [3:0] green_o); 24 | 25 | localparam NUM_PIXELS = 8; // How many are computed in parallel 26 | localparam PIXEL_WIDTH = 12; 27 | 28 | wire in_visible_region; 29 | wire pixel_out; 30 | wire new_frame; 31 | wire fifo_almost_empty; 32 | wire fifo_empty; 33 | wire[NUM_PIXELS * PIXEL_WIDTH - 1:0] fifo_in; 34 | wire[PIXEL_WIDTH - 1:0] fifo_out; 35 | wire pixels_ready; 36 | wire start_next_batch = pixels_ready && (fifo_almost_empty || fifo_empty) 37 | && pixel_out; 38 | 39 | vga_timing_generator vga_timing_generator( 40 | .clk(clk), 41 | .vsync_o(vsync_o), 42 | .hsync_o(hsync_o), 43 | .in_visible_region(in_visible_region), 44 | .pixel_out(pixel_out), 45 | .new_frame(new_frame)); 46 | 47 | pixel_fifo #(.NUM_PIXELS(NUM_PIXELS), .PIXEL_WIDTH(PIXEL_WIDTH)) pixel_fifo( 48 | .clk(clk), 49 | .reset(new_frame), 50 | .almost_empty(fifo_almost_empty), 51 | .empty(fifo_empty), 52 | .enqueue(start_next_batch), 53 | .value_in(fifo_in), 54 | .dequeue(pixel_out), 55 | .value_out(fifo_out)); 56 | 57 | pixel_processor #(.NUM_PIXELS(NUM_PIXELS), .PIXEL_WIDTH(PIXEL_WIDTH)) pixel_processor( 58 | .clk(clk), 59 | .new_frame(new_frame), 60 | .result(fifo_in), 61 | .start_next_batch(start_next_batch), 62 | .result_ready(pixels_ready)); 63 | 64 | assign red_o = in_visible_region ? fifo_out[11:8] : 0; 65 | assign blue_o = in_visible_region ? fifo_out[7:4] : 0; 66 | assign green_o = in_visible_region ? fifo_out[3:0] : 0; 67 | 68 | endmodule 69 | -------------------------------------------------------------------------------- /vga_timing_generator.v: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2013 Jeff Bush 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | 18 | 19 | module vga_timing_generator( 20 | input clk, 21 | output reg vsync_o, 22 | output reg hsync_o, 23 | output in_visible_region, 24 | output pixel_out, 25 | output new_frame); 26 | 27 | // 640x480 @60 hz. Pixel clock = 25.175 Mhz Vert Refresh = 31.46875 kHz 28 | // Horizontal timing: 29 | // front porch 16 clocks 30 | // sync pulse 96 clocks 31 | // back porch 48 clocks 32 | // visible area 640 clocks 33 | // total 800 clocks 34 | // 35 | // Vertical timing: 36 | // front porch 10 lines 37 | // sync pulse 2 lines 38 | // back porch 33 lines 39 | // visible area 480 lines 40 | // total 525 lines 41 | parameter HSYNC_START = 16; // Front Porch 42 | parameter HSYNC_END = HSYNC_START + 96; 43 | parameter HVISIBLE_START = HSYNC_END + 48; // Back Porch 44 | parameter HVISIBLE_END = HVISIBLE_START + 640; 45 | parameter VSYNC_START = 10; // Front Porch 46 | parameter VSYNC_END = VSYNC_START + 2; 47 | parameter VVISIBLE_START = VSYNC_END + 33; // Back Porch 48 | parameter VVISIBLE_END = VVISIBLE_START + 480; 49 | 50 | reg hvisible; 51 | reg vvisible; 52 | reg[10:0] horizontal_counter; 53 | reg[10:0] vertical_counter; 54 | reg dot_enable = 0; 55 | 56 | assign pixel_out = dot_enable && in_visible_region; 57 | assign in_visible_region = hvisible && vvisible; 58 | 59 | initial 60 | begin 61 | vsync_o = 0; 62 | hsync_o = 0; 63 | hvisible = 0; 64 | vvisible = 0; 65 | horizontal_counter = 0; 66 | vertical_counter = 0; 67 | end 68 | 69 | wire hvisible_end = horizontal_counter == HVISIBLE_END; 70 | wire vvisible_end = vertical_counter == VVISIBLE_END; 71 | assign new_frame = horizontal_counter == 0 && vertical_counter == 0; 72 | 73 | 74 | always @(posedge clk) 75 | begin 76 | // Divide clock rate by two 77 | dot_enable <= !dot_enable; 78 | 79 | if (dot_enable) 80 | begin 81 | // Counters 82 | if (hvisible_end) 83 | begin 84 | horizontal_counter <= 0; 85 | hvisible <= 0; 86 | if (vvisible_end) 87 | begin 88 | vvisible <= 0; 89 | vertical_counter <= 0; 90 | end 91 | else 92 | vertical_counter <= vertical_counter + 1; 93 | end 94 | else 95 | horizontal_counter <= horizontal_counter + 1; 96 | 97 | if (vertical_counter == VSYNC_START) 98 | vsync_o <= 0; 99 | else if (vertical_counter == VSYNC_END) 100 | vsync_o <= 1; 101 | else if (vertical_counter == VVISIBLE_START) 102 | vvisible <= 1; 103 | 104 | if (horizontal_counter == HSYNC_START) 105 | hsync_o <= 0; 106 | else if (horizontal_counter == HSYNC_END) 107 | hsync_o <= 1; 108 | else if (horizontal_counter == HVISIBLE_START) 109 | hvisible <= 1; 110 | end 111 | end 112 | endmodule 113 | --------------------------------------------------------------------------------