From 823db1be621d512a0c5e18289c2a857606961460 Mon Sep 17 00:00:00 2001 From: brad Date: Tue, 2 Jan 2007 16:24:48 +0000 Subject: [PATCH] initial --- v/char_rom.v | 1167 +++++++++++++++++++++++++++++++++++++++++++++ v/crt.v | 465 ++++++++++++++++++ v/fpga.ucf | 27 ++ v/fpga.v | 89 ++++ v/fpga2.ucf | 27 ++ v/fpga2.v | 100 ++++ v/fpga3.v | 109 +++++ v/programramdac.v | 208 ++++++++ v/ps2.v | 160 +++++++ v/run.v | 101 ++++ v/scancode.v | 372 +++++++++++++++ v/scancode2.v | 252 ++++++++++ v/scancode_rom.v | 132 +++++ v/vga.v | 224 +++++++++ v/vgacore.v | 153 ++++++ v/video_mem.v | 23 + v/video_ram.v | 32 ++ v/xsa-200.ucf | 17 + 18 files changed, 3658 insertions(+) create mode 100644 v/char_rom.v create mode 100644 v/crt.v create mode 100644 v/fpga.ucf create mode 100644 v/fpga.v create mode 100644 v/fpga2.ucf create mode 100644 v/fpga2.v create mode 100644 v/fpga3.v create mode 100644 v/programramdac.v create mode 100644 v/ps2.v create mode 100644 v/run.v create mode 100644 v/scancode.v create mode 100644 v/scancode2.v create mode 100644 v/scancode_rom.v create mode 100644 v/vga.v create mode 100644 v/vgacore.v create mode 100644 v/video_mem.v create mode 100644 v/video_ram.v create mode 100644 v/xsa-200.ucf diff --git a/v/char_rom.v b/v/char_rom.v new file mode 100644 index 0000000..47b7f55 --- /dev/null +++ b/v/char_rom.v @@ -0,0 +1,1167 @@ +// +module char_rom(addr, data); + + input [9:0] addr; + output reg [7:0] data; + + always @(addr) + case (addr) + + // char 0, '.' + 0: data <= 8'h00; + 1: data <= 8'h00; + 2: data <= 8'h00; + 3: data <= 8'h00; + 4: data <= 8'h00; + 5: data <= 8'h00; + 6: data <= 8'h00; + 7: data <= 8'h00; + // char 1, '.' + 8: data <= 8'h00; + 9: data <= 8'h00; + 10: data <= 8'h00; + 11: data <= 8'h00; + 12: data <= 8'h00; + 13: data <= 8'h00; + 14: data <= 8'h00; + 15: data <= 8'h00; + // char 2, '.' + 16: data <= 8'h00; + 17: data <= 8'h00; + 18: data <= 8'h00; + 19: data <= 8'h00; + 20: data <= 8'h00; + 21: data <= 8'h00; + 22: data <= 8'h00; + 23: data <= 8'h00; + // char 3, '.' + 24: data <= 8'h00; + 25: data <= 8'h00; + 26: data <= 8'h00; + 27: data <= 8'h00; + 28: data <= 8'h00; + 29: data <= 8'h00; + 30: data <= 8'h00; + 31: data <= 8'h00; + // char 4, '.' + 32: data <= 8'h00; + 33: data <= 8'h00; + 34: data <= 8'h00; + 35: data <= 8'h00; + 36: data <= 8'h00; + 37: data <= 8'h00; + 38: data <= 8'h00; + 39: data <= 8'h00; + // char 5, '.' + 40: data <= 8'h00; + 41: data <= 8'h00; + 42: data <= 8'h00; + 43: data <= 8'h00; + 44: data <= 8'h00; + 45: data <= 8'h00; + 46: data <= 8'h00; + 47: data <= 8'h00; + // char 6, '.' + 48: data <= 8'h00; + 49: data <= 8'h00; + 50: data <= 8'h00; + 51: data <= 8'h00; + 52: data <= 8'h00; + 53: data <= 8'h00; + 54: data <= 8'h00; + 55: data <= 8'h00; + // char 7, '.' + 56: data <= 8'h00; + 57: data <= 8'h00; + 58: data <= 8'h00; + 59: data <= 8'h00; + 60: data <= 8'h00; + 61: data <= 8'h00; + 62: data <= 8'h00; + 63: data <= 8'h00; + // char 8, '.' + 64: data <= 8'h00; + 65: data <= 8'h00; + 66: data <= 8'h00; + 67: data <= 8'h00; + 68: data <= 8'h00; + 69: data <= 8'h00; + 70: data <= 8'h00; + 71: data <= 8'h00; + // char 9, '.' + 72: data <= 8'h00; + 73: data <= 8'h00; + 74: data <= 8'h00; + 75: data <= 8'h00; + 76: data <= 8'h00; + 77: data <= 8'h00; + 78: data <= 8'h00; + 79: data <= 8'h00; + // char 10, '.' + 80: data <= 8'h00; + 81: data <= 8'h00; + 82: data <= 8'h00; + 83: data <= 8'h00; + 84: data <= 8'h00; + 85: data <= 8'h00; + 86: data <= 8'h00; + 87: data <= 8'h00; + // char 11, '.' + 88: data <= 8'h00; + 89: data <= 8'h00; + 90: data <= 8'h00; + 91: data <= 8'h00; + 92: data <= 8'h00; + 93: data <= 8'h00; + 94: data <= 8'h00; + 95: data <= 8'h00; + // char 12, '.' + 96: data <= 8'h00; + 97: data <= 8'h00; + 98: data <= 8'h00; + 99: data <= 8'h00; + 100: data <= 8'h00; + 101: data <= 8'h00; + 102: data <= 8'h00; + 103: data <= 8'h00; + // char 13, '.' + 104: data <= 8'h00; + 105: data <= 8'h00; + 106: data <= 8'h00; + 107: data <= 8'h00; + 108: data <= 8'h00; + 109: data <= 8'h00; + 110: data <= 8'h00; + 111: data <= 8'h00; + // char 14, '.' + 112: data <= 8'h00; + 113: data <= 8'h00; + 114: data <= 8'h00; + 115: data <= 8'h00; + 116: data <= 8'h00; + 117: data <= 8'h00; + 118: data <= 8'h00; + 119: data <= 8'h00; + // char 15, '.' + 120: data <= 8'h00; + 121: data <= 8'h00; + 122: data <= 8'h00; + 123: data <= 8'h00; + 124: data <= 8'h00; + 125: data <= 8'h00; + 126: data <= 8'h00; + 127: data <= 8'h00; + // char 16, '.' + 128: data <= 8'h00; + 129: data <= 8'h00; + 130: data <= 8'h00; + 131: data <= 8'h00; + 132: data <= 8'h00; + 133: data <= 8'h00; + 134: data <= 8'h00; + 135: data <= 8'h00; + // char 17, '.' + 136: data <= 8'h00; + 137: data <= 8'h00; + 138: data <= 8'h00; + 139: data <= 8'h00; + 140: data <= 8'h00; + 141: data <= 8'h00; + 142: data <= 8'h00; + 143: data <= 8'h00; + // char 18, '.' + 144: data <= 8'h00; + 145: data <= 8'h00; + 146: data <= 8'h00; + 147: data <= 8'h00; + 148: data <= 8'h00; + 149: data <= 8'h00; + 150: data <= 8'h00; + 151: data <= 8'h00; + // char 19, '.' + 152: data <= 8'h00; + 153: data <= 8'h00; + 154: data <= 8'h00; + 155: data <= 8'h00; + 156: data <= 8'h00; + 157: data <= 8'h00; + 158: data <= 8'h00; + 159: data <= 8'h00; + // char 20, '.' + 160: data <= 8'h00; + 161: data <= 8'h00; + 162: data <= 8'h00; + 163: data <= 8'h00; + 164: data <= 8'h00; + 165: data <= 8'h00; + 166: data <= 8'h00; + 167: data <= 8'h00; + // char 21, '.' + 168: data <= 8'h00; + 169: data <= 8'h00; + 170: data <= 8'h00; + 171: data <= 8'h00; + 172: data <= 8'h00; + 173: data <= 8'h00; + 174: data <= 8'h00; + 175: data <= 8'h00; + // char 22, '.' + 176: data <= 8'h00; + 177: data <= 8'h00; + 178: data <= 8'h00; + 179: data <= 8'h00; + 180: data <= 8'h00; + 181: data <= 8'h00; + 182: data <= 8'h00; + 183: data <= 8'h00; + // char 23, '.' + 184: data <= 8'h00; + 185: data <= 8'h00; + 186: data <= 8'h00; + 187: data <= 8'h00; + 188: data <= 8'h00; + 189: data <= 8'h00; + 190: data <= 8'h00; + 191: data <= 8'h00; + // char 24, '.' + 192: data <= 8'h00; + 193: data <= 8'h00; + 194: data <= 8'h00; + 195: data <= 8'h00; + 196: data <= 8'h00; + 197: data <= 8'h00; + 198: data <= 8'h00; + 199: data <= 8'h00; + // char 25, '.' + 200: data <= 8'h00; + 201: data <= 8'h00; + 202: data <= 8'h00; + 203: data <= 8'h00; + 204: data <= 8'h00; + 205: data <= 8'h00; + 206: data <= 8'h00; + 207: data <= 8'h00; + // char 26, '.' + 208: data <= 8'h00; + 209: data <= 8'h00; + 210: data <= 8'h00; + 211: data <= 8'h00; + 212: data <= 8'h00; + 213: data <= 8'h00; + 214: data <= 8'h00; + 215: data <= 8'h00; + // char 27, '.' + 216: data <= 8'h00; + 217: data <= 8'h00; + 218: data <= 8'h00; + 219: data <= 8'h00; + 220: data <= 8'h00; + 221: data <= 8'h00; + 222: data <= 8'h00; + 223: data <= 8'h00; + // char 28, '.' + 224: data <= 8'h00; + 225: data <= 8'h00; + 226: data <= 8'h00; + 227: data <= 8'h00; + 228: data <= 8'h00; + 229: data <= 8'h00; + 230: data <= 8'h00; + 231: data <= 8'h00; + // char 29, '.' + 232: data <= 8'h00; + 233: data <= 8'h00; + 234: data <= 8'h00; + 235: data <= 8'h00; + 236: data <= 8'h00; + 237: data <= 8'h00; + 238: data <= 8'h00; + 239: data <= 8'h00; + // char 30, '.' + 240: data <= 8'h00; + 241: data <= 8'h00; + 242: data <= 8'h00; + 243: data <= 8'h00; + 244: data <= 8'h00; + 245: data <= 8'h00; + 246: data <= 8'h00; + 247: data <= 8'h00; + // char 31, '.' + 248: data <= 8'h00; + 249: data <= 8'h00; + 250: data <= 8'h00; + 251: data <= 8'h00; + 252: data <= 8'h00; + 253: data <= 8'h00; + 254: data <= 8'h00; + 255: data <= 8'h00; + // char 32, ' ' + 256: data <= 8'h00; + 257: data <= 8'h00; + 258: data <= 8'h00; + 259: data <= 8'h00; + 260: data <= 8'h00; + 261: data <= 8'h00; + 262: data <= 8'h00; + 263: data <= 8'h00; + // char 33, '!' + 264: data <= 8'h10; + 265: data <= 8'h38; + 266: data <= 8'h38; + 267: data <= 8'h10; + 268: data <= 8'h10; + 269: data <= 8'h00; + 270: data <= 8'h10; + 271: data <= 8'h00; + // char 34, '"' + 272: data <= 8'h6c; + 273: data <= 8'h6c; + 274: data <= 8'h48; + 275: data <= 8'h00; + 276: data <= 8'h00; + 277: data <= 8'h00; + 278: data <= 8'h00; + 279: data <= 8'h00; + // char 35, '#' + 280: data <= 8'h00; + 281: data <= 8'h28; + 282: data <= 8'h7c; + 283: data <= 8'h28; + 284: data <= 8'h28; + 285: data <= 8'h7c; + 286: data <= 8'h28; + 287: data <= 8'h00; + // char 36, '$' + 288: data <= 8'h10; // ...X.... 10 + 289: data <= 8'h7e; // .XXXXXX. 7e + 290: data <= 8'h90; // X..X.... 90 + 291: data <= 8'h7c; // .XXXXX.. 7c + 292: data <= 8'h12; // ...X..X. 12 + 293: data <= 8'hfc; // XXXXXX.. fc + 294: data <= 8'h10; // ...X.... 10 + 295: data <= 8'h00; // ........ 00 + // char 37, '%' + 296: data <= 8'h62; // .XX...X. 62 + 297: data <= 8'h94; // X..X.X.. 94 + 298: data <= 8'h68; // .XX.X... 68 + 299: data <= 8'h10; // ...X.... 10 + 300: data <= 8'h26; // ..X..XX. 26 + 301: data <= 8'h49; // .X..X..X 49 + 302: data <= 8'h86; // X....XX. 86 + 303: data <= 8'h00; // ........ 00 + // char 38, '&' + 304: data <= 8'h30; // ..XX.... 30 + 305: data <= 8'h48; // .X..X... 48 + 306: data <= 8'h30; // ..XX.... 30 + 307: data <= 8'h48; // .X..X... 48 + 308: data <= 8'h85; // X....X.X 85 + 309: data <= 8'h46; // .X...XX. 46 + 310: data <= 8'h39; // ..XXX..X 39 + 311: data <= 8'h00; // ........ 00 + // char 39, ''' + 312: data <= 8'h30; + 313: data <= 8'h30; + 314: data <= 8'h20; + 315: data <= 8'h00; + 316: data <= 8'h00; + 317: data <= 8'h00; + 318: data <= 8'h00; + 319: data <= 8'h00; + // char 40, '(' + 320: data <= 8'h10; + 321: data <= 8'h20; + 322: data <= 8'h20; + 323: data <= 8'h20; + 324: data <= 8'h20; + 325: data <= 8'h20; + 326: data <= 8'h10; + 327: data <= 8'h00; + // char 41, ')' + 328: data <= 8'h20; + 329: data <= 8'h10; + 330: data <= 8'h10; + 331: data <= 8'h10; + 332: data <= 8'h10; + 333: data <= 8'h10; + 334: data <= 8'h20; + 335: data <= 8'h00; + // char 42, '*' + 336: data <= 8'h99; // X..XX..X 00 + 337: data <= 8'h5a; // .X.XX.X. 00 + 338: data <= 8'h3c; // ..XXXX.. 00 + 339: data <= 8'hff; // XXXXXXXX 00 + 340: data <= 8'h3c; // ..XXXX.. 00 + 341: data <= 8'h5a; // .X.XX.X. 00 + 342: data <= 8'h99; // X..XX..X 00 + 343: data <= 8'h00; // ........ 00 + // char 43, '+' + 344: data <= 8'h00; + 345: data <= 8'h10; + 346: data <= 8'h10; + 347: data <= 8'h7c; + 348: data <= 8'h10; + 349: data <= 8'h10; + 350: data <= 8'h00; + 351: data <= 8'h00; + // char 44, ',' + 352: data <= 8'h00; + 353: data <= 8'h00; + 354: data <= 8'h00; + 355: data <= 8'h00; + 356: data <= 8'h00; + 357: data <= 8'h30; + 358: data <= 8'h30; + 359: data <= 8'h20; + // char 45, '-' + 360: data <= 8'h00; + 361: data <= 8'h00; + 362: data <= 8'h00; + 363: data <= 8'h7c; + 364: data <= 8'h00; + 365: data <= 8'h00; + 366: data <= 8'h00; + 367: data <= 8'h00; + // char 46, '.' + 368: data <= 8'h00; + 369: data <= 8'h00; + 370: data <= 8'h00; + 371: data <= 8'h00; + 372: data <= 8'h00; + 373: data <= 8'h30; + 374: data <= 8'h30; + 375: data <= 8'h00; + // char 47, '/' + 376: data <= 8'h00; + 377: data <= 8'h04; + 378: data <= 8'h08; + 379: data <= 8'h10; + 380: data <= 8'h20; + 381: data <= 8'h40; + 382: data <= 8'h00; + 383: data <= 8'h00; + // char 48, '0' + 384: data <= 8'h38; + 385: data <= 8'h44; + 386: data <= 8'h4c; + 387: data <= 8'h54; + 388: data <= 8'h64; + 389: data <= 8'h44; + 390: data <= 8'h38; + 391: data <= 8'h00; + // char 49, '1' + 392: data <= 8'h10; + 393: data <= 8'h30; + 394: data <= 8'h10; + 395: data <= 8'h10; + 396: data <= 8'h10; + 397: data <= 8'h10; + 398: data <= 8'h38; + 399: data <= 8'h00; + // char 50, '2' + 400: data <= 8'h38; + 401: data <= 8'h44; + 402: data <= 8'h04; + 403: data <= 8'h18; + 404: data <= 8'h20; + 405: data <= 8'h40; + 406: data <= 8'h7c; + 407: data <= 8'h00; + // char 51, '3' + 408: data <= 8'h38; + 409: data <= 8'h44; + 410: data <= 8'h04; + 411: data <= 8'h38; + 412: data <= 8'h04; + 413: data <= 8'h44; + 414: data <= 8'h38; + 415: data <= 8'h00; + // char 52, '4' + 416: data <= 8'h08; + 417: data <= 8'h18; + 418: data <= 8'h28; + 419: data <= 8'h48; + 420: data <= 8'h7c; + 421: data <= 8'h08; + 422: data <= 8'h08; + 423: data <= 8'h00; + // char 53, '5' + 424: data <= 8'h7c; + 425: data <= 8'h40; + 426: data <= 8'h40; + 427: data <= 8'h78; + 428: data <= 8'h04; + 429: data <= 8'h44; + 430: data <= 8'h38; + 431: data <= 8'h00; + // char 54, '6' + 432: data <= 8'h18; + 433: data <= 8'h20; + 434: data <= 8'h40; + 435: data <= 8'h78; + 436: data <= 8'h44; + 437: data <= 8'h44; + 438: data <= 8'h38; + 439: data <= 8'h00; + // char 55, '7' + 440: data <= 8'h7c; + 441: data <= 8'h04; + 442: data <= 8'h08; + 443: data <= 8'h10; + 444: data <= 8'h20; + 445: data <= 8'h20; + 446: data <= 8'h20; + 447: data <= 8'h00; + // char 56, '8' + 448: data <= 8'h38; + 449: data <= 8'h44; + 450: data <= 8'h44; + 451: data <= 8'h38; + 452: data <= 8'h44; + 453: data <= 8'h44; + 454: data <= 8'h38; + 455: data <= 8'h00; + // char 57, '9' + 456: data <= 8'h38; + 457: data <= 8'h44; + 458: data <= 8'h44; + 459: data <= 8'h3c; + 460: data <= 8'h04; + 461: data <= 8'h08; + 462: data <= 8'h30; + 463: data <= 8'h00; + // char 58, ':' + 464: data <= 8'h00; + 465: data <= 8'h00; + 466: data <= 8'h30; + 467: data <= 8'h30; + 468: data <= 8'h00; + 469: data <= 8'h30; + 470: data <= 8'h30; + 471: data <= 8'h00; + // char 59, ';' + 472: data <= 8'h00; + 473: data <= 8'h00; + 474: data <= 8'h30; + 475: data <= 8'h30; + 476: data <= 8'h00; + 477: data <= 8'h30; + 478: data <= 8'h30; + 479: data <= 8'h20; + // char 60, '<' + 480: data <= 8'h08; + 481: data <= 8'h10; + 482: data <= 8'h20; + 483: data <= 8'h40; + 484: data <= 8'h20; + 485: data <= 8'h10; + 486: data <= 8'h08; + 487: data <= 8'h00; + // char 61, '=' + 488: data <= 8'h00; + 489: data <= 8'h00; + 490: data <= 8'h7c; + 491: data <= 8'h00; + 492: data <= 8'h00; + 493: data <= 8'h7c; + 494: data <= 8'h00; + 495: data <= 8'h00; + // char 62, '>' + 496: data <= 8'h20; + 497: data <= 8'h10; + 498: data <= 8'h08; + 499: data <= 8'h04; + 500: data <= 8'h08; + 501: data <= 8'h10; + 502: data <= 8'h20; + 503: data <= 8'h00; + // char 63, '?' + 504: data <= 8'h38; + 505: data <= 8'h44; + 506: data <= 8'h04; + 507: data <= 8'h18; + 508: data <= 8'h10; + 509: data <= 8'h00; + 510: data <= 8'h10; + 511: data <= 8'h00; + // char 64, '@' + 512: data <= 8'h38; + 513: data <= 8'h44; + 514: data <= 8'h5c; + 515: data <= 8'h54; + 516: data <= 8'h5c; + 517: data <= 8'h40; + 518: data <= 8'h38; + 519: data <= 8'h00; + // char 65, 'A' + 520: data <= 8'h38; + 521: data <= 8'h44; + 522: data <= 8'h44; + 523: data <= 8'h44; + 524: data <= 8'h7c; + 525: data <= 8'h44; + 526: data <= 8'h44; + 527: data <= 8'h00; + // char 66, 'B' + 528: data <= 8'h78; + 529: data <= 8'h44; + 530: data <= 8'h44; + 531: data <= 8'h78; + 532: data <= 8'h44; + 533: data <= 8'h44; + 534: data <= 8'h78; + 535: data <= 8'h00; + // char 67, 'C' + 536: data <= 8'h38; + 537: data <= 8'h44; + 538: data <= 8'h40; + 539: data <= 8'h40; + 540: data <= 8'h40; + 541: data <= 8'h44; + 542: data <= 8'h38; + 543: data <= 8'h00; + // char 68, 'D' + 544: data <= 8'h78; + 545: data <= 8'h44; + 546: data <= 8'h44; + 547: data <= 8'h44; + 548: data <= 8'h44; + 549: data <= 8'h44; + 550: data <= 8'h78; + 551: data <= 8'h00; + // char 69, 'E' + 552: data <= 8'h7c; + 553: data <= 8'h40; + 554: data <= 8'h40; + 555: data <= 8'h78; + 556: data <= 8'h40; + 557: data <= 8'h40; + 558: data <= 8'h7c; + 559: data <= 8'h00; + // char 70, 'F' + 560: data <= 8'h7c; + 561: data <= 8'h40; + 562: data <= 8'h40; + 563: data <= 8'h78; + 564: data <= 8'h40; + 565: data <= 8'h40; + 566: data <= 8'h40; + 567: data <= 8'h00; + // char 71, 'G' + 568: data <= 8'h38; + 569: data <= 8'h44; + 570: data <= 8'h40; + 571: data <= 8'h5c; + 572: data <= 8'h44; + 573: data <= 8'h44; + 574: data <= 8'h3c; + 575: data <= 8'h00; + // char 72, 'H' + 576: data <= 8'h44; + 577: data <= 8'h44; + 578: data <= 8'h44; + 579: data <= 8'h7c; + 580: data <= 8'h44; + 581: data <= 8'h44; + 582: data <= 8'h44; + 583: data <= 8'h00; + // char 73, 'I' + 584: data <= 8'h38; + 585: data <= 8'h10; + 586: data <= 8'h10; + 587: data <= 8'h10; + 588: data <= 8'h10; + 589: data <= 8'h10; + 590: data <= 8'h38; + 591: data <= 8'h00; + // char 74, 'J' + 592: data <= 8'h04; + 593: data <= 8'h04; + 594: data <= 8'h04; + 595: data <= 8'h04; + 596: data <= 8'h44; + 597: data <= 8'h44; + 598: data <= 8'h38; + 599: data <= 8'h00; + // char 75, 'K' + 600: data <= 8'h44; + 601: data <= 8'h48; + 602: data <= 8'h50; + 603: data <= 8'h60; + 604: data <= 8'h50; + 605: data <= 8'h48; + 606: data <= 8'h44; + 607: data <= 8'h00; + // char 76, 'L' + 608: data <= 8'h40; + 609: data <= 8'h40; + 610: data <= 8'h40; + 611: data <= 8'h40; + 612: data <= 8'h40; + 613: data <= 8'h40; + 614: data <= 8'h7c; + 615: data <= 8'h00; + // char 77, 'M' + 616: data <= 8'h44; + 617: data <= 8'h6c; + 618: data <= 8'h54; + 619: data <= 8'h44; + 620: data <= 8'h44; + 621: data <= 8'h44; + 622: data <= 8'h44; + 623: data <= 8'h00; + // char 78, 'N' + 624: data <= 8'h44; + 625: data <= 8'h64; + 626: data <= 8'h54; + 627: data <= 8'h4c; + 628: data <= 8'h44; + 629: data <= 8'h44; + 630: data <= 8'h44; + 631: data <= 8'h00; + // char 79, 'O' + 632: data <= 8'h38; + 633: data <= 8'h44; + 634: data <= 8'h44; + 635: data <= 8'h44; + 636: data <= 8'h44; + 637: data <= 8'h44; + 638: data <= 8'h38; + 639: data <= 8'h00; + // char 80, 'P' + 640: data <= 8'h78; + 641: data <= 8'h44; + 642: data <= 8'h44; + 643: data <= 8'h78; + 644: data <= 8'h40; + 645: data <= 8'h40; + 646: data <= 8'h40; + 647: data <= 8'h00; + // char 81, 'Q' + 648: data <= 8'h38; + 649: data <= 8'h44; + 650: data <= 8'h44; + 651: data <= 8'h44; + 652: data <= 8'h54; + 653: data <= 8'h48; + 654: data <= 8'h34; + 655: data <= 8'h00; + // char 82, 'R' + 656: data <= 8'h78; + 657: data <= 8'h44; + 658: data <= 8'h44; + 659: data <= 8'h78; + 660: data <= 8'h48; + 661: data <= 8'h44; + 662: data <= 8'h44; + 663: data <= 8'h00; + // char 83, 'S' + 664: data <= 8'h38; + 665: data <= 8'h44; + 666: data <= 8'h40; + 667: data <= 8'h38; + 668: data <= 8'h04; + 669: data <= 8'h44; + 670: data <= 8'h38; + 671: data <= 8'h00; + // char 84, 'T' + 672: data <= 8'h7c; + 673: data <= 8'h10; + 674: data <= 8'h10; + 675: data <= 8'h10; + 676: data <= 8'h10; + 677: data <= 8'h10; + 678: data <= 8'h10; + 679: data <= 8'h00; + // char 85, 'U' + 680: data <= 8'h44; + 681: data <= 8'h44; + 682: data <= 8'h44; + 683: data <= 8'h44; + 684: data <= 8'h44; + 685: data <= 8'h44; + 686: data <= 8'h38; + 687: data <= 8'h00; + // char 86, 'V' + 688: data <= 8'h44; + 689: data <= 8'h44; + 690: data <= 8'h44; + 691: data <= 8'h44; + 692: data <= 8'h44; + 693: data <= 8'h28; + 694: data <= 8'h10; + 695: data <= 8'h00; + // char 87, 'W' + 696: data <= 8'h44; + 697: data <= 8'h44; + 698: data <= 8'h54; + 699: data <= 8'h54; + 700: data <= 8'h54; + 701: data <= 8'h54; + 702: data <= 8'h28; + 703: data <= 8'h00; + // char 88, 'X' + 704: data <= 8'h44; + 705: data <= 8'h44; + 706: data <= 8'h28; + 707: data <= 8'h10; + 708: data <= 8'h28; + 709: data <= 8'h44; + 710: data <= 8'h44; + 711: data <= 8'h00; + // char 89, 'Y' + 712: data <= 8'h44; + 713: data <= 8'h44; + 714: data <= 8'h44; + 715: data <= 8'h28; + 716: data <= 8'h10; + 717: data <= 8'h10; + 718: data <= 8'h10; + 719: data <= 8'h00; + // char 90, 'Z' + 720: data <= 8'h78; + 721: data <= 8'h08; + 722: data <= 8'h10; + 723: data <= 8'h20; + 724: data <= 8'h40; + 725: data <= 8'h40; + 726: data <= 8'h78; + 727: data <= 8'h00; + // char 91, '[' + 728: data <= 8'h38; + 729: data <= 8'h20; + 730: data <= 8'h20; + 731: data <= 8'h20; + 732: data <= 8'h20; + 733: data <= 8'h20; + 734: data <= 8'h38; + 735: data <= 8'h00; + // char 92, '\' + 736: data <= 8'h00; + 737: data <= 8'h40; + 738: data <= 8'h20; + 739: data <= 8'h10; + 740: data <= 8'h08; + 741: data <= 8'h04; + 742: data <= 8'h00; + 743: data <= 8'h00; + // char 93, ']' + 744: data <= 8'h38; + 745: data <= 8'h08; + 746: data <= 8'h08; + 747: data <= 8'h08; + 748: data <= 8'h08; + 749: data <= 8'h08; + 750: data <= 8'h38; + 751: data <= 8'h00; + // char 94, '^' + 752: data <= 8'h10; + 753: data <= 8'h28; + 754: data <= 8'h44; + 755: data <= 8'h00; + 756: data <= 8'h00; + 757: data <= 8'h00; + 758: data <= 8'h00; + 759: data <= 8'h00; + // char 95, '_' + 760: data <= 8'h00; + 761: data <= 8'h00; + 762: data <= 8'h00; + 763: data <= 8'h00; + 764: data <= 8'h00; + 765: data <= 8'h00; + 766: data <= 8'h00; + 767: data <= 8'hfc; + // char 96, '`' + 768: data <= 8'h30; + 769: data <= 8'h30; + 770: data <= 8'h10; + 771: data <= 8'h00; + 772: data <= 8'h00; + 773: data <= 8'h00; + 774: data <= 8'h00; + 775: data <= 8'h00; + // char 97, 'a' + 776: data <= 8'h00; + 777: data <= 8'h00; + 778: data <= 8'h38; + 779: data <= 8'h04; + 780: data <= 8'h3c; + 781: data <= 8'h44; + 782: data <= 8'h3c; + 783: data <= 8'h00; + // char 98, 'b' + 784: data <= 8'h40; + 785: data <= 8'h40; + 786: data <= 8'h78; + 787: data <= 8'h44; + 788: data <= 8'h44; + 789: data <= 8'h44; + 790: data <= 8'h78; + 791: data <= 8'h00; + // char 99, 'c' + 792: data <= 8'h00; + 793: data <= 8'h00; + 794: data <= 8'h38; + 795: data <= 8'h44; + 796: data <= 8'h40; + 797: data <= 8'h44; + 798: data <= 8'h38; + 799: data <= 8'h00; + // char 100, 'd' + 800: data <= 8'h04; + 801: data <= 8'h04; + 802: data <= 8'h3c; + 803: data <= 8'h44; + 804: data <= 8'h44; + 805: data <= 8'h44; + 806: data <= 8'h3c; + 807: data <= 8'h00; + // char 101, 'e' + 808: data <= 8'h00; + 809: data <= 8'h00; + 810: data <= 8'h38; + 811: data <= 8'h44; + 812: data <= 8'h78; + 813: data <= 8'h40; + 814: data <= 8'h38; + 815: data <= 8'h00; + // char 102, 'f' + 816: data <= 8'h18; + 817: data <= 8'h20; + 818: data <= 8'h20; + 819: data <= 8'h78; + 820: data <= 8'h20; + 821: data <= 8'h20; + 822: data <= 8'h20; + 823: data <= 8'h00; + // char 103, 'g' + 824: data <= 8'h00; + 825: data <= 8'h00; + 826: data <= 8'h3c; + 827: data <= 8'h44; + 828: data <= 8'h44; + 829: data <= 8'h3c; + 830: data <= 8'h04; + 831: data <= 8'h38; + // char 104, 'h' + 832: data <= 8'h40; + 833: data <= 8'h40; + 834: data <= 8'h70; + 835: data <= 8'h48; + 836: data <= 8'h48; + 837: data <= 8'h48; + 838: data <= 8'h48; + 839: data <= 8'h00; + // char 105, 'i' + 840: data <= 8'h10; + 841: data <= 8'h00; + 842: data <= 8'h10; + 843: data <= 8'h10; + 844: data <= 8'h10; + 845: data <= 8'h10; + 846: data <= 8'h18; + 847: data <= 8'h00; + // char 106, 'j' + 848: data <= 8'h08; + 849: data <= 8'h00; + 850: data <= 8'h18; + 851: data <= 8'h08; + 852: data <= 8'h08; + 853: data <= 8'h08; + 854: data <= 8'h48; + 855: data <= 8'h30; + // char 107, 'k' + 856: data <= 8'h40; + 857: data <= 8'h40; + 858: data <= 8'h48; + 859: data <= 8'h50; + 860: data <= 8'h60; + 861: data <= 8'h50; + 862: data <= 8'h48; + 863: data <= 8'h00; + // char 108, 'l' + 864: data <= 8'h10; + 865: data <= 8'h10; + 866: data <= 8'h10; + 867: data <= 8'h10; + 868: data <= 8'h10; + 869: data <= 8'h10; + 870: data <= 8'h18; + 871: data <= 8'h00; + // char 109, 'm' + 872: data <= 8'h00; + 873: data <= 8'h00; + 874: data <= 8'h68; + 875: data <= 8'h54; + 876: data <= 8'h54; + 877: data <= 8'h44; + 878: data <= 8'h44; + 879: data <= 8'h00; + // char 110, 'n' + 880: data <= 8'h00; + 881: data <= 8'h00; + 882: data <= 8'h70; + 883: data <= 8'h48; + 884: data <= 8'h48; + 885: data <= 8'h48; + 886: data <= 8'h48; + 887: data <= 8'h00; + // char 111, 'o' + 888: data <= 8'h00; + 889: data <= 8'h00; + 890: data <= 8'h38; + 891: data <= 8'h44; + 892: data <= 8'h44; + 893: data <= 8'h44; + 894: data <= 8'h38; + 895: data <= 8'h00; + // char 112, 'p' + 896: data <= 8'h00; + 897: data <= 8'h00; + 898: data <= 8'h78; + 899: data <= 8'h44; + 900: data <= 8'h44; + 901: data <= 8'h44; + 902: data <= 8'h78; + 903: data <= 8'h40; + // char 113, 'q' + 904: data <= 8'h00; + 905: data <= 8'h00; + 906: data <= 8'h3c; + 907: data <= 8'h44; + 908: data <= 8'h44; + 909: data <= 8'h44; + 910: data <= 8'h3c; + 911: data <= 8'h04; + // char 114, 'r' + 912: data <= 8'h00; + 913: data <= 8'h00; + 914: data <= 8'h58; + 915: data <= 8'h24; + 916: data <= 8'h20; + 917: data <= 8'h20; + 918: data <= 8'h70; + 919: data <= 8'h00; + // char 115, 's' + 920: data <= 8'h00; + 921: data <= 8'h00; + 922: data <= 8'h38; + 923: data <= 8'h40; + 924: data <= 8'h38; + 925: data <= 8'h04; + 926: data <= 8'h38; + 927: data <= 8'h00; + // char 116, 't' + 928: data <= 8'h00; + 929: data <= 8'h20; + 930: data <= 8'h78; + 931: data <= 8'h20; + 932: data <= 8'h20; + 933: data <= 8'h28; + 934: data <= 8'h10; + 935: data <= 8'h00; + // char 117, 'u' + 936: data <= 8'h00; + 937: data <= 8'h00; + 938: data <= 8'h48; + 939: data <= 8'h48; + 940: data <= 8'h48; + 941: data <= 8'h58; + 942: data <= 8'h28; + 943: data <= 8'h00; + // char 118, 'v' + 944: data <= 8'h00; + 945: data <= 8'h00; + 946: data <= 8'h44; + 947: data <= 8'h44; + 948: data <= 8'h44; + 949: data <= 8'h28; + 950: data <= 8'h10; + 951: data <= 8'h00; + // char 119, 'w' + 952: data <= 8'h00; + 953: data <= 8'h00; + 954: data <= 8'h44; + 955: data <= 8'h44; + 956: data <= 8'h54; + 957: data <= 8'h7c; + 958: data <= 8'h28; + 959: data <= 8'h00; + // char 120, 'x' + 960: data <= 8'h00; + 961: data <= 8'h00; + 962: data <= 8'h48; + 963: data <= 8'h48; + 964: data <= 8'h30; + 965: data <= 8'h48; + 966: data <= 8'h48; + 967: data <= 8'h00; + // char 121, 'y' + 968: data <= 8'h00; + 969: data <= 8'h00; + 970: data <= 8'h48; + 971: data <= 8'h48; + 972: data <= 8'h48; + 973: data <= 8'h38; + 974: data <= 8'h10; + 975: data <= 8'h60; + // char 122, 'z' + 976: data <= 8'h00; + 977: data <= 8'h00; + 978: data <= 8'h78; + 979: data <= 8'h08; + 980: data <= 8'h30; + 981: data <= 8'h40; + 982: data <= 8'h78; + 983: data <= 8'h00; + // char 123, '{' + 984: data <= 8'h18; + 985: data <= 8'h20; + 986: data <= 8'h20; + 987: data <= 8'h60; + 988: data <= 8'h20; + 989: data <= 8'h20; + 990: data <= 8'h18; + 991: data <= 8'h00; + // char 124, '|' + 992: data <= 8'h10; + 993: data <= 8'h10; + 994: data <= 8'h10; + 995: data <= 8'h00; + 996: data <= 8'h10; + 997: data <= 8'h10; + 998: data <= 8'h10; + 999: data <= 8'h00; + // char 125, '}' + 1000: data <= 8'h30; + 1001: data <= 8'h08; + 1002: data <= 8'h08; + 1003: data <= 8'h0c; + 1004: data <= 8'h08; + 1005: data <= 8'h08; + 1006: data <= 8'h30; + 1007: data <= 8'h00; + // char 126, '~' + 1008: data <= 8'h28; + 1009: data <= 8'h50; + 1010: data <= 8'h00; + 1011: data <= 8'h00; + 1012: data <= 8'h00; + 1013: data <= 8'h00; + 1014: data <= 8'h00; + 1015: data <= 8'h00; + // char 127, '.' + 1016: data <= 8'h00; + 1017: data <= 8'h00; + 1018: data <= 8'h00; + 1019: data <= 8'h00; + 1020: data <= 8'h00; + 1021: data <= 8'h00; + 1022: data <= 8'h00; + 1023: data <= 8'h00; + +// 10'h000: data <= 8'h00; + default: data <= 8'h00; + endcase + +endmodule diff --git a/v/crt.v b/v/crt.v new file mode 100644 index 0000000..b697231 --- /dev/null +++ b/v/crt.v @@ -0,0 +1,465 @@ +// crt.v + +// +// Extremely basic crt ram management +// +// insert character +// check for printable +// check for cr,lf +// scroll one line +// advance start +// clear last line +// move to begining of line +// lf +// move to next line +// cr +// move to begining of line +// + +module crt(reset_n, clock, + insert, done, data, clearing, + ram_addr, ram_data, ram_we_n, ram_wclk, ram_wslot, + cursorh, cursorv); + + input reset_n; + input clock; + input insert; + output reg done; + output reg clearing; + input [7:0] data; + output [11:0] ram_addr; + output reg [7:0] ram_data; + output reg ram_we_n; + output [6:0] cursorh; + output [5:0] cursorv; + output reg ram_wclk; + input ram_wslot; + + reg [6:0] cursor_h; + reg [5:0] cursor_v; + + // conditions + wire eol, scroll; + + // output of state machine + reg inc_h, clr_h; + reg inc_v, clr_v; + reg set_newline; + reg set_done; + + // external state + reg newline; + + reg [11:0] offset; + reg inc_offset, clr_offset; + + reg [3:0] state, nextstate; + wire printable; + + reg [2:0] write_delay; + + // offset + v*80 + h + // factored into (v*64 + v*16) + h + assign ram_addr = +//debug /*offset +*/ + {cursor_v, 6'b0} + {2'b0, cursor_v, 4'b0} + + {4'b000, cursor_h}; + + assign printable = ((data[6:0] > 7'h20) && (data[6:0] < 7'h7f)) ? + 1'b1 : 1'b0; + + // one-hot states + parameter [3:0] + T_RESET = 4'd0, + T_CLEARALL = 4'd1, + T_CLEARALL_NEXT = 4'd2, + T_IDLE = 4'd3, + T_PREWRITE = 4'd4, + T_WRITE = 4'd5, + T_POSTWRITE = 4'd6, + T_NEWLINE = 4'd7, + T_SCROLL = 4'd8, + T_CLEARLAST = 4'd9, + T_CLEARLAST_WRITE = 4'd10, + T_CLEARLAST_NEXT = 4'd11, + T_CLEARLAST_DONE = 4'd12; + + + // don't change unless you fix the factored *80 in ram_addr + parameter [6:0] COLS = 80; + parameter [5:0] LINES = 25; + + // manage incrementing offset + always @(posedge clock or negedge reset_n) + if (~reset_n) + offset <= 12'b0; + else + begin + if (inc_offset) + if (offset == (LINES-1)*COLS) + offset <= 12'b0; + else + offset <= offset + COLS; + end + + // manage clearing offset when it rolls over + always @(posedge clock or negedge reset_n) + if (~reset_n) + clr_offset <= 0; + else + begin + if (offset >= LINES*COLS) + clr_offset <= 1; + else + clr_offset <= 0; + end + + // manage incrementing h + always @(posedge clock or negedge reset_n) + if (~reset_n) + cursor_h <= 7'b0; + else + begin + if (inc_h) + cursor_h <= cursor_h + 1; + else + if (clr_h) + cursor_h <= 7'b0; + end + + // manage incrementing v + always @(posedge clock or negedge reset_n) + if (~reset_n) + cursor_v <= 6'b0; + else + begin + if (inc_v) + cursor_v <= cursor_v + 1; + else + if (clr_v) + cursor_v <= 6'b0; + end + + // manage end of line + assign eol = cursor_h == (COLS-1); + + // and end of screen + assign scroll = cursor_v == (LINES-1); + + // manage external state + always @(posedge clock or negedge reset_n) + if (~reset_n) + begin + ram_data <= 8'b0; + done <= 1'b0; + newline <= 1'b0; + write_delay <= 3'b0; + end + else + begin + //debug + if (state == T_CLEARALL_NEXT) + begin + if (cursor_h == 7'd1) + ram_data <= 8'h30 + {5'b0, cursor_v[5:3]}; + else + if (cursor_h == 7'd2) + ram_data <= 8'h30 + {5'b0, cursor_v[2:0]}; + else + ram_data <= 8'h30 + {1'b0, cursor_h}; +// ram_data <= 8'h40; +// ram_data <= 8'h20 + {1'b0, cursor_h} + { 2'b00, cursor_v}; + end + + if (state == T_CLEARLAST_WRITE) + ram_data <= 8'b0; + + if (state == T_IDLE) + begin + newline <= 1'b0; + done <= 1'b0; + if (insert) + ram_data <= data; + end + + if (state == T_WRITE || + state == T_CLEARLAST_WRITE || + state == T_CLEARALL) + write_delay <= write_delay + 1; + else + write_delay <= 3'd0; + + if (set_newline) + newline <= 1'b1; + + if (set_done) + done <= 1'b1; + end + + // next state + always @(posedge clock or negedge reset_n) + if (~reset_n) + state <= T_RESET; + else + state <= nextstate; + + // insert state machine + always @(state or insert or eol or newline or scroll or + printable or data or write_delay or ram_wslot) + begin + inc_h = 1'b0; + clr_h = 1'b0; + inc_v = 1'b0; + clr_v = 1'b0; + inc_offset = 1'b0; + set_newline = 1'b0; + set_done = 1'b0; + clearing = 1'b0; + ram_we_n = 1'b1; + + case (state) // synthesis full_case + T_RESET: + begin + clearing = 1'b1; + nextstate = T_CLEARALL; +nextstate = T_IDLE; + end + + T_CLEARALL: + begin + clearing = 1'b1; + ram_we_n = 1'b0; + ram_wclk = (write_delay == 3'd1) ? 1 : 0; + nextstate = (write_delay == 3'd1) ? T_CLEARALL_NEXT : T_CLEARALL; + end + + T_CLEARALL_NEXT: + begin + clearing = 1'b1; + nextstate = T_CLEARALL; + + if (eol) + begin +//debug +// if (cursor_v == 15) + if (scroll) + begin + clr_v = 1'b1; + clr_h = 1'b1; + nextstate = T_IDLE; + end + else + begin + clr_h = 1'b1; + inc_v = 1'b1; + end + end + else + inc_h = 1'b1; + end + + T_IDLE: + begin + clearing = 1'b0; + if (insert) + begin + // if printable character, write to ram + if (printable) + nextstate = T_PREWRITE; + else + nextstate = T_POSTWRITE; + end + else + nextstate = T_IDLE; + end + + T_PREWRITE: + begin + // wait until we're in the write slot + nextstate = ram_wslot ? T_WRITE : T_PREWRITE; + + if (eol) + begin + set_newline = 1'b1; + clr_h = 1'b1; + end + end + + T_WRITE: + begin + ram_we_n = 1'b0; + + // delay until ram_wclk catches up + ram_wclk = (write_delay == 3'd1) ? 1 : 0; + nextstate = (write_delay == 3'd1) ? T_POSTWRITE : T_WRITE; + end + + T_POSTWRITE: + begin + set_done = 1'b1; + + // cr + if (data[6:0] == 7'h0d) + clr_h = 1'b1; + else + inc_h = 1'b1; + + // eol or lf + if (newline || (data[6:0] == 7'h0a)) + nextstate = T_NEWLINE; + else + nextstate = T_IDLE; + end + + T_NEWLINE: + begin + clr_h = 1'b1; + + if (scroll) + nextstate = T_SCROLL; + else + begin + inc_v = 1'b1; + nextstate = T_IDLE; + end + + end + + T_SCROLL: + begin + clr_h = 1'b1; + inc_offset = 1'b1; + nextstate = T_CLEARLAST; + end + + T_CLEARLAST: + begin + nextstate = T_CLEARLAST_WRITE; + end + + T_CLEARLAST_WRITE: + begin + ram_we_n = 1'b0; + + // delay until ram_wclk catches up + ram_wclk = (write_delay == 3'd1) ? 1 : 0; + nextstate = (write_delay == 3'd1) ? + T_CLEARLAST_NEXT : T_CLEARLAST_WRITE; + end + + T_CLEARLAST_NEXT: + begin + inc_h = 1'b1; + ram_we_n = 1'b1; + nextstate = T_CLEARLAST_WRITE; + + if (eol) + nextstate = T_CLEARLAST_DONE; + end + + T_CLEARLAST_DONE: + begin + clr_h = 1'b1; + nextstate = T_IDLE; + end + endcase + end + + assign cursorh = cursor_h; + assign cursorv = cursor_v; + +endmodule + + +// ------------------ + +//`define TEST +`ifdef TEST + +`timescale 1ns / 1ns + +module test; + + reg clk, reset_n; + reg insert; + wire done; + reg [7:0] data; + integer count; + reg free; + + + wire [11:0] ram_addr; + wire [7:0] ram_data; + wire ram_we_n; + wire [6:0] cursorh; + wire [5:0] cursorv; + + crt _crt(reset_n, clk, + insert, done, data, + ram_addr, ram_data, ram_we_n, + cursorh, cursorv); + +// defparam _crt.LINES = 4; +// defparam _crt.COLS = 4; + + initial + begin + $timeformat(-9, 0, "ns", 7); + + $dumpfile("crt.vcd"); + $dumpvars(0, test._crt); + end + + always @(posedge done) + if (free) + begin + data = 8'h41 + count; + count = count + 1; + if (count == 3) + begin + count = 0; + data = 8'o212; + end + end + + initial + begin + clk = 0; + reset_n = 1; + insert = 0; + data = 0; + count = 0; + free = 0; + + #1 reset_n = 0; + #100 reset_n = 1; + + #200 begin insert = 1; data = 8'h41; end + #80 insert = 0; + + #200 begin insert = 1; data = 8'h42; end + #80 insert = 0; + + #200 begin insert = 1; data = 8'h0d; end + #80 insert = 0; + + #200 begin insert = 1; data = 8'h0a; end + #80 insert = 0; + + #200 begin insert = 1; data = 8'h43; end + #80 insert = 0; + +// #50000 $finish; + #10000 $finish; + end + + always + begin + #20 clk = 0; + #20 clk = 1; + end + +endmodule + +`endif // `ifdef TEST + diff --git a/v/fpga.ucf b/v/fpga.ucf new file mode 100644 index 0000000..6d57f56 --- /dev/null +++ b/v/fpga.ucf @@ -0,0 +1,27 @@ +net clka loc=r8; +#net clkb loc=b8; +net reset_n loc=e3; + +net ps2_clk loc=f4; +net ps2_data loc=e1; + +net vga_blue0 loc=h4; +net vga_blue1 loc=k3; +net vga_blue2 loc=l5; +net vga_green0 loc=h2; +net vga_green1 loc=k5; +net vga_green2 loc=r1; +net vga_red0 loc=j1; +net vga_red1 loc=m1; +net vga_red2 loc=t2; +net vga_hsync_n loc=k4; +net vga_vsync_n loc=k1; + +net fpga_din_d0 loc=d14; +net fpga_d1 loc=e16; +net fpga_d2 loc=f15; +net fpga_d3 loc=g16; +net fpga_d4 loc=j16; +net fpga_d5 loc=m16; +net fpga_d6 loc=n16; +net fpga_d7 loc=n14; diff --git a/v/fpga.v b/v/fpga.v new file mode 100644 index 0000000..33cf78a --- /dev/null +++ b/v/fpga.v @@ -0,0 +1,89 @@ +// fpga.v + +module fpga (clka, + reset_n, + ps2_clk, + ps2_data, + vga_blue0, + vga_blue1, + vga_blue2, + vga_green0, + vga_green1, + vga_green2, + vga_red0, + vga_red1, + vga_red2, + vga_hsync_n, + vga_vsync_n, + fpga_din_d0, + fpga_d1, + fpga_d2, + fpga_d3, + fpga_d4, + fpga_d5, + fpga_d6, + fpga_d7 + ); + + input clka; // 100mhz + input reset_n; + + input ps2_clk, ps2_data; + + output vga_blue0, vga_blue1, vga_blue2; + output vga_green0, vga_green1, vga_green2; + output vga_red0, vga_red1, vga_red2; + output vga_hsync_n, vga_vsync_n; + + output fpga_din_d0, fpga_d1, fpga_d2, fpga_d3, + fpga_d4, fpga_d5, fpga_d6, fpga_d7; + + // + wire hsync, vsync; + wire [8:0] pixel; + + // + wire [7:0] led_data; + + // signals to create a 25MHz clock from the 100MHz input clock + wire clk25; + reg [1:0] gray_cnt; + + // clock divider by 4 to for a slower clock + // uses grey code for minimized logic + always @(posedge clka or negedge reset_n) + if (~reset_n) + gray_cnt <= 2'b00; + else + case (gray_cnt) + 2'b00: gray_cnt <= 2'b01; + 2'b01: gray_cnt <= 2'b11; + 2'b11: gray_cnt <= 2'b10; + 2'b10: gray_cnt <= 2'b00; + default: gray_cnt <= 2'b00; + endcase + + // assign 25mhz clock + assign clk25 = gray_cnt[1]; + + vga vga (.reset_n(reset_n), + .clock(clk25), + .pixel(pixel), + .blank_n(), + .hsync(hsync), + .vsync(vsync), + .ps2_clk(ps2_clk), + .ps2_data(ps2_data), + .led_data(led_data)); + + assign vga_hsync_n = ~hsync; + assign vga_vsync_n = ~vsync; + + assign {vga_red2, vga_red1, vga_red0, + vga_green2, vga_green1, vga_green0, + vga_blue2, vga_blue1, vga_blue0} = pixel; + + assign {fpga_din_d0, fpga_d1, fpga_d2, fpga_d3, + fpga_d4, fpga_d5, fpga_d6, fpga_d7} = led_data; + +endmodule // fpga diff --git a/v/fpga2.ucf b/v/fpga2.ucf new file mode 100644 index 0000000..bc993fc --- /dev/null +++ b/v/fpga2.ucf @@ -0,0 +1,27 @@ +net clka loc=r8; +#net clkb loc=b8; +net reset_n loc=e3; + +net ps2_clk loc=f4; +net ps2_data loc=e1; + +#net vga_blue0 loc=h4; +#net vga_blue1 loc=k3; +#net vga_blue2 loc=l5; +#net vga_green0 loc=h2; +#net vga_green1 loc=k5; +#net vga_green2 loc=r1; +#net vga_red0 loc=j1; +#net vga_red1 loc=m1; +#net vga_red2 loc=t2; +#net vga_hsync_n loc=k4; +#net vga_vsync_n loc=k1; + +net fpga_din_d0 loc=d14; +net fpga_d1 loc=e16; +net fpga_d2 loc=f15; +net fpga_d3 loc=g16; +net fpga_d4 loc=j16; +net fpga_d5 loc=m16; +net fpga_d6 loc=n16; +net fpga_d7 loc=n14; diff --git a/v/fpga2.v b/v/fpga2.v new file mode 100644 index 0000000..7c143c5 --- /dev/null +++ b/v/fpga2.v @@ -0,0 +1,100 @@ +// fpga2.v + +// simple test module for ps2 module + +module fpga (clka, + clkb, + reset_n, + ps2_clk, + ps2_data, + vga_blue0, + vga_blue1, + vga_blue2, + vga_green0, + vga_green1, + vga_green2, + vga_red0, + vga_red1, + vga_red2, + vga_hsync_n, + vga_vsync_n, + fpga_din_d0, + fpga_d1, + fpga_d2, + fpga_d3, + fpga_d4, + fpga_d5, + fpga_d6, + fpga_d7 + ); + + input clka; // 100mhz + input clkb; // 50mhz + input reset_n; + + input ps2_clk, ps2_data; + + output vga_blue0, vga_blue1, vga_blue2; + output vga_green0, vga_green1, vga_green2; + output vga_red0, vga_red1, vga_red2; + output vga_hsync_n, vga_vsync_n; + + output fpga_din_d0, fpga_d1, fpga_d2, fpga_d3, + fpga_d4, fpga_d5, fpga_d6, fpga_d7; + + // + wire hsync, vsync; + wire [8:0] pixel; + + // signals to create a 25MHz clock from the 100MHz input clock + wire clk25; + reg [1:0] gray_cnt; + + // clock divider by 4 to for a slower clock + // uses grey code for minimized logic + always @(posedge clka or negedge reset_n) + if (~reset_n) + gray_cnt <= 2'b00; + else + case (gray_cnt) + 2'b00: gray_cnt <= 2'b01; + 2'b01: gray_cnt <= 2'b11; + 2'b11: gray_cnt <= 2'b10; + 2'b10: gray_cnt <= 2'b00; + default: gray_cnt <= 2'b00; + endcase + + // assign the clock that this entity runs off + assign clk25 = gray_cnt[1]; + + wire kb_rdy; + wire kb_bsy; + wire [7:0] kb_scancode; + + wire [7:0] data; + + ps2 ps2(.clk(clk25), + .rst_n(reset_n), + .ps2_clk(ps2_clk), + .ps2_data(ps2_data), + .scancode(kb_scancode), + .parity(), + .busy(kb_bsy), + .rdy(kb_rdy), + .error()); + + //xc2s200-5fg256 + + reg rdy; + + always @(posedge kb_rdy or negedge reset_n) + if (~reset_n) + rdy <= 0; + else + rdy <= ~rdy; + + assign {fpga_din_d0, fpga_d1, fpga_d2, fpga_d3, + fpga_d4, fpga_d5, fpga_d6, fpga_d7} = + { kb_scancode[6], rdy, kb_scancode[5:0] }; + +endmodule // fpga diff --git a/v/fpga3.v b/v/fpga3.v new file mode 100644 index 0000000..05dd6e4 --- /dev/null +++ b/v/fpga3.v @@ -0,0 +1,109 @@ +// fpga2.v + +// simple test module for scancode module + +module fpga (clka, + clkb, + reset_n, + ps2_clk, + ps2_data, + vga_blue0, + vga_blue1, + vga_blue2, + vga_green0, + vga_green1, + vga_green2, + vga_red0, + vga_red1, + vga_red2, + vga_hsync_n, + vga_vsync_n, + fpga_din_d0, + fpga_d1, + fpga_d2, + fpga_d3, + fpga_d4, + fpga_d5, + fpga_d6, + fpga_d7 + ); + + input clka; // 100mhz + input clkb; // 50mhz + input reset_n; + + input ps2_clk, ps2_data; + + output vga_blue0, vga_blue1, vga_blue2; + output vga_green0, vga_green1, vga_green2; + output vga_red0, vga_red1, vga_red2; + output vga_hsync_n, vga_vsync_n; + + output fpga_din_d0, fpga_d1, fpga_d2, fpga_d3, + fpga_d4, fpga_d5, fpga_d6, fpga_d7; + + // + wire hsync, vsync; + wire [8:0] pixel; + + // signals to create a 25MHz clock from the 100MHz input clock + wire clk25; + reg [1:0] gray_cnt; + + // clock divider by 4 to for a slower clock + // uses grey code for minimized logic + always @(posedge clka or negedge reset_n) + if (~reset_n) + gray_cnt <= 2'b00; + else + case (gray_cnt) + 2'b00: gray_cnt <= 2'b01; + 2'b01: gray_cnt <= 2'b11; + 2'b11: gray_cnt <= 2'b10; + 2'b10: gray_cnt <= 2'b00; + default: gray_cnt <= 2'b00; + endcase + + // assign the clock that this entity runs off + assign clk25 = gray_cnt[1]; + + reg [7:0] kb_scancode; + reg kb_rdy; + + wire [7:0] kb_ascii; + wire kb_release; + wire kb_ascii_rdy; + + scancode_convert scancode_convert(.clock(clk25), + .reset_n(reset_n), + .scancode(kb_scancode), + .ascii(kb_ascii), + .key_up(kb_release), + .strobe_in(kb_rdy), + .strobe_out(kb_ascii_rdy)); + + //xc2s200-5fg256 + + reg [2:0] clk8; + always @(posedge clk25 or negedge reset_n) + if (~reset_n) + clk8 = 3'b111; + else + clk8 = clk8 + 1; + + always @(posedge clk25 or negedge reset_n) + if (~reset_n) + kb_scancode = 0; + else + if (clk8 == 8'b111) + begin + kb_scancode = kb_scancode + 1; + kb_rdy = 1; + end + else + kb_rdy = 0; + + assign {fpga_din_d0, fpga_d1, fpga_d2, fpga_d3, + fpga_d4, fpga_d5, fpga_d6, fpga_d7} = kb_ascii; + +endmodule // fpga diff --git a/v/programramdac.v b/v/programramdac.v new file mode 100644 index 0000000..4a290ab --- /dev/null +++ b/v/programramdac.v @@ -0,0 +1,208 @@ +// programramdac.v + +// hardcoded values for initialising the RAMDAC +module dac_data(addr, o); + + input[2:0] addr; + output o; + reg [7:0] o; + + // hard code initial control register programming values + // DAC(76543210) + always @(addr) + case (addr) + 3'd0: o <= 8'b10000001; // Cmd reg A, high colour dual edged mode + 3'd1: o <= 8'b00000000; // Pallette address reg gets $00 + 3'd2: o <= 8'b11111111; // Read mask reg gets $FF + 3'd3: o <= 8'b00000010; // Pallette address reg gets $02 + 3'd4: o <= 8'b00000010; // Command reg B gets $02 + 3'd5: o <= 8'b00000000; // Pallette address reg gets $00 + endcase // case(addr) +endmodule + +module dac_rs(addr, o); + + input[2:0] addr; + output o; + reg [2:0] o; + + // RS(210) + always @(addr) + case (addr) + 3'd0: o <= 3'b110; // RS gets Command reg A + 3'd1: o <= 3'b000; // RS gets Pallette address reg + 3'd2: o <= 3'b010; // RS gets Read mask reg + 3'd3: o <= 3'b000; // RS gets Pallette address reg + 3'd4: o <= 3'b010; // RS gets Command reg B + 3'd5: o <= 3'b000; // RS gets Pallette address reg + endcase +endmodule + +module programramdac(rstn, clk, start, done, WRn, RDn, RS, data); + + input rstn; + input clk; + input start; // start signal + output done; + output WRn; // write line to ramdac + output RDn; // read line ot ramdac + input [2:0] RS; // register select lines to ramdac + inout [7:0] data; // data lines to ramdac + + reg done, WRn; + + // FSM states for the main mealy FSM + parameter [2:0] + stIdle = 3'd0, + stWrite = 3'd1, + stWrCycle = 3'd2, + stNextWrite = 3'd3; + + reg [2:0] presState, nextState; + + // initCnt controls write state + reg [2:0] initCnt; + reg increment; + + // signals to create a 12.5MHz clock from the 50MHz input clock + wire divclk; + reg [1:0] gray_cnt; + + // create signals so the data and RS lines can be used as tristate + // buffers. this is important as they share lines with the ethernet PHY + reg [7:0] prgData; + reg [2:0] prgRS; + reg latchData; + reg latchRS; + + wire [7:0] theData; + wire [2:0] theRs; + + + // data and register select + dac_data arraydac(.addr(initCnt), .o(theData)); + dac_rs arrayrs(.addr(initCnt), .o(theRS)); + + // clock divider by 4 to for a slower clock to avoid timing violations + // uses grey code for minimized logic + always @(posedge clk or rstn) + if (~rstn) + gray_cnt <= 2'b00; + else + case (gray_cnt) + 2'b00: gray_cnt <= 2'b01; + 2'b01: gray_cnt <= 2'b11; + 2'b11: gray_cnt <= 2'b10; + 2'b10: gray_cnt <= 2'b00; + default: gray_cnt <= 2'b00; + endcase + + // assign the clock that this entity runs off + assign divclk = gray_cnt[1]; + + // read isn't needed, tie high + assign RDn = 1; + + // main clocked process + always @(posedge divclk or rstn) + if (~rstn) + begin + presState <= stIdle; + initCnt <= 0; + end + else + if (divclk == 1) + begin + presState <= nextState; + if (increment) + if (initCnt < 5) + initCnt <= initCnt + 1; + else + initCnt <= 0; + end + + // Main FSM process + always @(presState, start, initCnt) + begin + // default signals and outputs for each FSM state + // note that the latch data and rs signals are defaulted to 1, + // so are only 0 in the idle state + WRn <= 1; + increment <= 0; + prgData <= 0; + prgRS <= 3'b001; + latchData <= 1; + latchRS <= 1; + done <= 0; + + case (presState) + stIdle: + begin + // wait for start signal from another process + if (start) + begin + nextState <= stWrite; + // setup for the first write to the RAMDAC for use + // by setting the register select lines and + // the data lines + prgRS <= theRS; + prgData <= theData; + end + else + begin + nextState <= stIdle; + latchData <= 0; + latchRS <=0; + end + end + + stWrite: + begin + // hold the register select and data lines for + // the write cycle and set the active low write signal + nextState <= stWrCycle; + prgRS <= theRS; + prgData <= theData; + WRn <= 0; + end + + stWrCycle: + begin + // continue if all 5 registers have been programmed + if (initCnt == 5) + begin + nextState <= stIdle; + done <= 1; + end + else + // continue writing to the registers + nextState <= stNextWrite; + + // hold the data to be sure the hold times aren't violated + prgRS <= theRS; + prgData <= theData; + + // increment initCnt to program the next register + increment <= 1; + end + + stNextWrite: + begin + nextState <= stWrite; + // setup for the next write cycle + prgRS <= theRS; + prgData <= theData; + end + + endcase; + + // assign data and RS prgData and prgRS respectively when they + // need to be latched otherwise keep them at high impedance + // to create a tri state buffer + end // always @ (presState, start, initCnt) + + assign data = latchData ? prgData : 8'bz; + assign RS = latchRS ? prgRS : 3'bz; + +endmodule + diff --git a/v/ps2.v b/v/ps2.v new file mode 100644 index 0000000..366ab54 --- /dev/null +++ b/v/ps2.v @@ -0,0 +1,160 @@ +// ps2.v +// +// Monitor the serial datastream and clock from a PS/2 keyboard +// and output a scancode for any key that is pressed. +// +// Notes: +// +// The clock from the PS/2 keyboard is used directly. It is sampled at +// the frequency of the main clock input; edges are extracted from the +// sample clock. The main clock must be substantially faster than +// the 10 KHz PS/2 clock - 200 KHz or more. +// +// The scancode is only valid when the ready signal is high. The scancode +// should be registered by an external circuit on the first clock edge +// after the ready signal goes high. +// +// The ready signal pulses only after the key is released. +// +// The error flag is set whenever the PS/2 clock stops pulsing and the +// PS/2 clock is either at a low level or less than 11 bits of serial +// data have been received (start + 8 data + parity + stop). +// + +module ps2(clk, // main clock + rst_n, // asynchronous reset + ps2_clk, // clock from keyboard + ps2_data, // data from keyboard + scancode, // key scancode + parity, // parity bit for scancode + busy, // busy receiving scancode + rdy, // scancode ready pulse + error // error receiving scancode + ); + + input clk, rst_n; + input ps2_clk; + input ps2_data; + output [7:0] scancode; + output parity; + output busy; + output rdy; + output error; + + + parameter FREQ = 25000; // frequency of the main clock (KHz) + parameter PS2_FREQ = 10; // keyboard clock frequency (KHz) + parameter TIMEOUT = FREQ / PS2_FREQ; // ps2_clk quiet timeout + parameter [7:0] KEY_RELEASE = 8'b11110000; // sent when key is rel'd + + reg [13:0] timer_r; // time since last PS/2 clock edge + wire [13:0] timer_x; + + reg [3:0] bitcnt_r; // number of received scancode bits + wire [3:0] bitcnt_x; + + reg [4:0] ps2_clk_r; // PS/2 clock sync / edge detect + wire [4:0] ps2_clk_x; + + reg [9:0] sc_r; // scancode shift register + wire [9:0] sc_x; + + reg keyrel_r; // set when key release received + wire keyrel_x; + + reg rdy_r; // set when scancode ready + wire rdy_x; + + reg error_r; // set when an error occurs + wire error_x; + + wire ps2_clk_fall_edge; // on falling edge of PS/2 clock + wire ps2_clk_rise_edge; // on rising edge of PS/2 clock + wire ps2_clk_edge; // on either edge of PS/2 clock + wire ps2_clk_quiet; // when no edges on PS/2 clock for TIMEOUT + wire scancode_rdy; // when scancode has been received + + + // shift the level on the PS/2 clock into a shift register + assign ps2_clk_x = {ps2_clk_r[3:0], ps2_clk}; + + // look at the PS/2 clock levels stored in the shift register + // and find rising or falling edges + assign ps2_clk_fall_edge = ps2_clk_r[4:1] == 4'b1100; + assign ps2_clk_rise_edge = ps2_clk_r[4:1] == 4'b0011; + assign ps2_clk_edge = ps2_clk_fall_edge || ps2_clk_rise_edge; + + // shift the keyboard scancode into the shift register on the + // falling edge of the PS/2 clock + assign sc_x = ps2_clk_fall_edge ? {ps2_data, sc_r[9:1]} : sc_r; + + // clear the timer right after a PS/2 clock edge and + // then keep incrementing it until the next edge + assign timer_x = ps2_clk_edge ? 0 : (timer_r + 1); + + // indicate when the PS/2 clock has stopped pulsing and + // is at a high level. + assign ps2_clk_quiet = timer_r == TIMEOUT && ps2_clk_r[1]; + + // increment bit counter on each falling edge of the PS/2 clock. + // reset the bit counter if the PS/2 clock stops pulsing or + // if there was an error receiving the scancode. + // otherwise, keep the bit counter unchanged. + assign bitcnt_x = ps2_clk_fall_edge ? (bitcnt_r + 1) : + (ps2_clk_quiet || error_r) ? 0 : + bitcnt_r; + + // a scancode has been received if the bit counter is 11 and + // the PS/2 clock has stopped pulsing + assign scancode_rdy = bitcnt_r == 4'd11 && ps2_clk_quiet; + +/* + // look for the scancode sent when the key is released + assign keyrel_x = (sc_r[7:0] == KEY_RELEASE && scancode_rdy) ? 1 : + (rdy_r || error_r) ? 0 : + keyrel_r; + + // the scancode for the pressed key arrives after receiving + // the key-release scancode + assign rdy_x = keyrel_r && scancode_rdy; +*/ + assign rdy_x = scancode_rdy; + + // indicate an error if the clock is low for too long or + // if it stops pulsing in the middle of a scancode + assign error_x = (timer_r == TIMEOUT && ps2_clk_r[1] == 0) || + (ps2_clk_quiet && bitcnt_r != 4'd11 && bitcnt_r != 4'd0) ? + 1 : error_r; + + // outputs + assign scancode = sc_r[7:0]; // scancode + assign parity = sc_r[8]; // parity bit for the scancode + assign busy = bitcnt_r != 4'd0; // busy when recv'ing scancode + assign rdy = rdy_r; // scancode ready flag + assign error = error_r; // error flag + + // update the various registers + always @(posedge clk or negedge rst_n) + if (~rst_n) + begin + ps2_clk_r <= 5'b11111; // assume PS/2 clock has been high + sc_r <= 0; + keyrel_r <= 0; + rdy_r <= 0; + timer_r <= 0; + bitcnt_r <= 0; + error_r <= 0; + end + else + begin + ps2_clk_r <= ps2_clk_x; + sc_r <= sc_x; + keyrel_r <= keyrel_x; + rdy_r <= rdy_x; + timer_r <= timer_x; + bitcnt_r <= bitcnt_x; + error_r <= error_x; + end + + +endmodule diff --git a/v/run.v b/v/run.v new file mode 100644 index 0000000..31c13aa --- /dev/null +++ b/v/run.v @@ -0,0 +1,101 @@ +// run.v + +`include "vga.v" + +`timescale 1ns / 1ns + +module test; + + reg clk, reset_n; + + wire [8:0] pixel; + wire blank_n; + wire hsync; + wire vsync; + reg ps2_clk; + reg ps2_data; + wire [7:0] led_data; + + vga vga(.reset_n(reset_n), + .clock(clk25), + .pixel(pixel), + .blank_n(blank_n), + .hsync(hsync), + .vsync(vsync), + .ps2_clk(ps2_clk), + .ps2_data(ps2_data), + .led_data(led_data)); + + // clock divider by 4 to for a slower clock + // uses grey code for minimized logic + reg [1:0] gray_cnt; + + always @(posedge clk or negedge reset_n) + if (~reset_n) + gray_cnt <= 2'b00; + else + case (gray_cnt) + 2'b00: gray_cnt <= 2'b01; + 2'b01: gray_cnt <= 2'b11; + 2'b11: gray_cnt <= 2'b10; + 2'b10: gray_cnt <= 2'b00; + default: gray_cnt <= 2'b00; + endcase + + wire clk25; + assign clk25 = gray_cnt[1]; + + initial + begin + $timeformat(-9, 0, "ns", 7); + + $dumpfile("vga.vcd"); +// $dumpvars(0, test.vga); + $dumpvars(0, test); + end + + initial + begin + clk = 0; + reset_n = 1; + ps2_clk <= 0; + ps2_data <= 0; + + #1 begin + reset_n = 0; + end + + #100 begin + reset_n = 1; + end + + #400000 + begin + vga.scancode_convert.strobe_out = 1; + vga.crt_data = 8'h41; + vga.scancode_convert.ascii = 8'h41; + end + #200 vga.scancode_convert.strobe_out = 0; + + #400 + begin + vga.scancode_convert.strobe_out = 1; + vga.crt_data = 8'h42; + vga.scancode_convert.ascii = 8'h42; + end + #200 vga.scancode_convert.strobe_out = 0; + +// #100000 $finish; +// #500000 $finish; +// #1000000 $finish; + #20000000 $finish; + end + + always + begin + #5 clk = 0; + #5 clk = 1; + end + +endmodule + diff --git a/v/scancode.v b/v/scancode.v new file mode 100644 index 0000000..beba5c1 --- /dev/null +++ b/v/scancode.v @@ -0,0 +1,372 @@ +// scancode.v + +// +// simple AT style keyboard scancode to ascii convertion +// keeps track of shift, capslock and control keys +// inputs scancodes and outputs ascii +// + +`include "scancode_rom.v" + +module scancode_convert(clock, + reset_n, + scancode, + ascii, + key_up, + strobe_in, + strobe_out); + + input clock; + input reset_n; + input [7:0] scancode; + output reg [7:0] ascii; + output reg key_up; + input strobe_in; + output reg strobe_out; + + // one-hot state machine states + parameter [2:0] + C_INIT = 3'd0, + C_IDLE = 3'd1, + C_KEYPRESS = 3'd2, + C_KEYRELEASE = 3'd3, + C_RELEASE = 3'd4, + C_HOLD = 3'd5; + + // + reg [2:0] state, nextstate; + + reg release_prefix; + reg release_prefix_set, release_prefix_clear; + + reg shift; + reg shift_set, shift_clear; + + reg ctrl; + reg ctrl_set, ctrl_clear; + + reg capslock; + reg capslock_toggle; + + reg strobe_out_set, strobe_out_clear; + reg key_up_set, key_up_clear; + + reg [2:0] hold_count; + + reg [6:0] sc; + wire [7:0] rom_data; + wire raise; + + // convert scancodes (plus shift/control) into ascii + scancode_rom scancode_rom(.addr({raise,sc}), + .data(rom_data)); + + assign raise = shift | capslock | ctrl; + + // internal state + always @(posedge clock or negedge reset_n) + if (~reset_n) + begin + shift <= 1'b0; + capslock <= 1'b0; + ctrl <= 1'b0; + + release_prefix <= 1'b0; + strobe_out <= 1'b0; + key_up <= 1'b0; + + hold_count <= 3'b0; + end + else + begin + if (shift_set) + shift <= 1'b1; + else + if (shift_clear) + shift <= 1'b0; + + if (ctrl_set) + ctrl <= 1'b1; + else + if (ctrl_clear) + ctrl <= 1'b0; + + if (capslock_toggle) + capslock <= ~capslock; + + if (release_prefix_set) + release_prefix <= 1'b1; + else + if (release_prefix_clear) + release_prefix <= 1'b0; + + if (strobe_out_set) + strobe_out <= 1'b1; + else + if (strobe_out_clear) + strobe_out <= 1'b0; + + if (key_up_set) + key_up <= 1'b1; + else + if (key_up_clear) + key_up <= 1'b0; + + // + if (state == C_HOLD) + hold_count <= hold_count + 1; + else + hold_count <= 3'd0; + end + + // next state + always @(posedge clock or negedge reset_n) + if (~reset_n) + state <= C_INIT; + else + state <= nextstate; + + always @(posedge clock) + if (state == C_IDLE && strobe_in) + sc <= scancode[6:0]; + + always @(posedge clock) + if (state == C_KEYPRESS || state == C_KEYRELEASE) + ascii <= ctrl ? (rom_data - 8'h40) : rom_data; + + always @(state or strobe_in or scancode or release_prefix or shift or ctrl or rom_data or hold_count) + begin + shift_set = 1'b0; + shift_clear = 1'b0; + ctrl_set = 1'b0; + ctrl_clear = 1'b0; + capslock_toggle = 1'b0; + release_prefix_set = 1'b0; + release_prefix_clear = 1'b0; + strobe_out_set = 1'b0; + strobe_out_clear = 1'b0; + key_up_set = 1'b0; + key_up_clear = 1'b0; + + case (state) // synthesis full_case + C_INIT: + begin + nextstate = C_IDLE; + end + + C_IDLE: + begin + if (strobe_in) + begin + case (scancode) + 8'hf0: /* release prefix */ + begin + release_prefix_set = 1'b1; + nextstate = C_IDLE; + end + + 8'h58: /* caps lock */ + begin + if (release_prefix) + capslock_toggle = 1'b1; + nextstate = C_RELEASE; + end + + 8'h12, /* left shift */ + 8'h59: /* right shift */ + begin + if (release_prefix) + shift_clear = 1'b1; + else + shift_set = 1'b1; + nextstate = C_RELEASE; + end + + 8'h14: /* left ctrl */ + begin + if (release_prefix) + ctrl_clear = 1'b1; + else + ctrl_set = 1'b1; + nextstate = C_RELEASE; + end + + default: + nextstate = release_prefix ? + C_KEYRELEASE : C_KEYPRESS; + endcase + end + else + nextstate = C_IDLE; + end + + C_KEYPRESS: + begin + strobe_out_set = 1'b1; + nextstate = C_RELEASE; + end + + C_KEYRELEASE: + begin +`ifdef SEND_KEYUP + strobe_out_set = 1'b1; +`endif + strobe_out_clear = 1'b1; + key_up_set = 1'b1; + nextstate = C_RELEASE; + end + + C_RELEASE: + begin + release_prefix_clear = 1'b1; + nextstate = C_HOLD; + end + + C_HOLD: + begin + nextstate = C_HOLD; + + if (hold_count == 3'd4) + begin + strobe_out_clear = 1'b1; + key_up_clear = 1'b1; + nextstate = C_IDLE; + end + end + + endcase + end + +endmodule + + +// ------------------ + +//`define TEST +`ifdef TEST + + +`timescale 1ns / 1ns + +module test; + + reg clk, reset_n; + reg [7:0] testcode; + wire [7:0] kb_ascii; + reg kb_rdy; + wire kb_release; + wire kb_ascii_rdy; + + scancode_convert scancode_convert(.clock(clk), + .reset_n(reset_n), + .scancode(testcode), + .ascii(kb_ascii), + .key_up(kb_release), + .strobe_in(kb_rdy), + .strobe_out(kb_ascii_rdy)); + + + initial + begin + $timeformat(-9, 0, "ns", 7); + + $dumpfile("scancode.vcd"); + $dumpvars(0, test.scancode_convert); + end + + initial + begin + clk = 1'b0; + reset_n = 1'b0; + kb_rdy = 1'b0; + + #200 begin + reset_n = 1'b1; + end + + // press "a" + #10 begin kb_rdy = 1'b1; testcode = 8'h1c; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end + #80 kb_rdy = 1'b0; + + // press "b" + #1000 begin kb_rdy = 1'b1; testcode = 8'h32; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h32; end + #80 kb_rdy = 1'b0; + + // enter + #1000 begin kb_rdy = 1'b1; testcode = 8'h5a; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h5a; end + #80 kb_rdy = 1'b0; + + // shift "a" + #1000 begin kb_rdy = 1'b1; testcode = 8'h12; end + #80 kb_rdy = 1'b0; + + #1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end + #80 kb_rdy = 1'b0; + + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h12; end + #80 kb_rdy = 1'b0; + + // press "c" + #1000 begin kb_rdy = 1'b1; testcode = 8'h21; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h21; end + #80 kb_rdy = 1'b0; + + // ctrl "a" + #1000 begin kb_rdy = 1'b1; testcode = 8'h14; end + #80 kb_rdy = 1'b0; + + #1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h1c; end + #80 kb_rdy = 1'b0; + + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h14; end + #80 kb_rdy = 1'b0; + + // press "d" + #1000 begin kb_rdy = 1'b1; testcode = 8'h23; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'hf0; end + #80 kb_rdy = 1'b0; + #1000 begin kb_rdy = 1'b1; testcode = 8'h23; end + #80 kb_rdy = 1'b0; + + + #5000 $finish; + end + + always + begin + #20 clk = 1'b0; + #20 clk = 1'b1; + end + +endmodule + +`endif + \ No newline at end of file diff --git a/v/scancode2.v b/v/scancode2.v new file mode 100644 index 0000000..4db3f8c --- /dev/null +++ b/v/scancode2.v @@ -0,0 +1,252 @@ +// scancode2.v + +// +// simple AT style keyboard scancode to ascii convertion +// keeps track of shift, capslock and control keys +// inputs scancodes and outputs ascii +// +// implicit state machine version +// + +`include "scancode_rom.v" + +module scancode_convert(clock, + reset_n, + scancode, + ascii, + key_up, + strobe_in, + strobe_out); + + input clock; + input reset_n; + input [7:0] scancode; + output reg [7:0] ascii; + output reg key_up; + input strobe_in; + output reg strobe_out; + + reg release_prefix; + reg shift; + reg ctrl; + reg capslock; + + reg [6:0] sc; + wire [7:0] rom_data; + wire raise; + + scancode_rom scancode_rom(.addr({raise,sc}), + .data(rom_data)); + + assign raise = shift | capslock | ctrl; + + always @(posedge clock or negedge reset_n) + if (~reset_n) + begin + shift <= 0; + ctrl <= 0; + capslock <= 0; + release_prefix <= 0; + key_up <= 0; + strobe_out <= 0; + end + else + begin + if (strobe_in) + begin + strobe_out <= 0; + + sc = scancode[6:0]; + case (scancode) + 8'hf0: /* release prefix */ + begin + @(posedge clock) release_prefix <= 1; + end + + 8'h58: /* caps lock */ + begin + if (~release_prefix) + @(posedge clock) capslock = ~capslock; + end + + 8'h12, /* left shift */ + 8'h59: /* right shift */ + begin + @(posedge clock) shift = release_prefix ? 0 : 1; + end + + 8'h14: /* left ctrl */ + begin + @(posedge clock) ctrl = release_prefix ? 0 : 1; + end + + default: + begin + if (release_prefix) + begin + @(posedge clock) + begin + ascii = ctrl ? (rom_data - 8'h40) : rom_data; + strobe_out <= 1; + end + @(posedge clock) strobe_out <= 1; + @(posedge clock) + begin + strobe_out <= 0; + release_prefix <= 0; + end + end + else + begin + @(posedge clock) + begin + /* + ascii = ctrl ? (rom_data - 8'h40) : rom_data; + strobe_out <= 1; + */ + key_up <= 1; + end + @(posedge clock) strobe_out <= 0; + @(posedge clock) + begin + strobe_out <= 0; + release_prefix <= 0; + end + end + end + endcase // case(scancode) + + end // if (strobe_in) + end // else: !if(~reset_n) + +endmodule + + +///* + + +// ------------------ + +`timescale 1ns / 1ns + +module test; + + reg clk, reset_n; + reg [7:0] testcode; + wire [7:0] kb_ascii; + reg kb_rdy; + wire kb_release; + wire kb_ascii_rdy; + + scancode_convert scancode_convert(.clock(clk), + .reset_n(reset_n), + .scancode(testcode), + .ascii(kb_ascii), + .key_up(kb_release), + .strobe_in(kb_rdy), + .strobe_out(kb_ascii_rdy)); + + + initial + begin + $timeformat(-9, 0, "ns", 7); + + $dumpfile("scancode.vcd"); + $dumpvars(0, test.scancode_convert); + end + + initial + begin + clk = 0; + reset_n = 0; + kb_rdy = 0; + + #200 begin + reset_n = 1; + end + + // press "a" + #10 begin kb_rdy = 1; testcode = 8'h1c; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h1c; end + #80 kb_rdy = 0; + + // press "b" + #1000 begin kb_rdy = 1; testcode = 8'h32; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h32; end + #80 kb_rdy = 0; + + // enter + #1000 begin kb_rdy = 1; testcode = 8'h5a; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h5a; end + #80 kb_rdy = 0; + + // shift "a" + #1000 begin kb_rdy = 1; testcode = 8'h12; end + #80 kb_rdy = 0; + + #1000 begin kb_rdy = 1; testcode = 8'h1c; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h1c; end + #80 kb_rdy = 0; + + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h12; end + #80 kb_rdy = 0; + + // press "c" + #1000 begin kb_rdy = 1; testcode = 8'h21; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h21; end + #80 kb_rdy = 0; + + // ctrl "a" + #1000 begin kb_rdy = 1; testcode = 8'h14; end + #80 kb_rdy = 0; + + #1000 begin kb_rdy = 1; testcode = 8'h1c; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h1c; end + #80 kb_rdy = 0; + + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h14; end + #80 kb_rdy = 0; + + // press "d" + #1000 begin kb_rdy = 1; testcode = 8'h23; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'hf0; end + #80 kb_rdy = 0; + #1000 begin kb_rdy = 1; testcode = 8'h23; end + #80 kb_rdy = 0; + + + #5000 $finish; + end + + always + begin + #40 clk = 0; + #40 clk = 1; + end + +endmodule + +//*/ \ No newline at end of file diff --git a/v/scancode_rom.v b/v/scancode_rom.v new file mode 100644 index 0000000..18904f0 --- /dev/null +++ b/v/scancode_rom.v @@ -0,0 +1,132 @@ +// scancode_rom.v + +module scancode_rom(addr, data); + + input [7:0] addr; + output reg [7:0] data; + + always @addr + case (addr) + 8'h0d: data <= 8'h07; /* TAB */ + 8'h0e: data <= "`"; + 8'h11: data <= 0; /* Left alt */ + 8'h12: data <= 0; /* Left shift */ + 8'h14: data <= 0; /* Left ctrl */ + 8'h15: data <= "q"; + 8'h16: data <= "1"; + 8'h1a: data <= "z"; + 8'h1b: data <= "s"; + 8'h1c: data <= 8'h61 /* "a" */; + 8'h1d: data <= "w"; + 8'h1e: data <= "2"; + + 8'h21: data <= "c"; + 8'h22: data <= "x"; + 8'h23: data <= "d"; + 8'h24: data <= "e"; + 8'h25: data <= "4"; + 8'h26: data <= "3"; + 8'h29: data <= " "; /* SPACE */ + 8'h2a: data <= "v"; + 8'h2b: data <= "f"; + 8'h2c: data <= "t"; + 8'h2d: data <= "r"; + 8'h2e: data <= "5"; + + 8'h31: data <= "n"; + 8'h32: data <= "b"; + 8'h33: data <= "h"; + 8'h34: data <= "g"; + 8'h35: data <= "y"; + 8'h36: data <= "6"; + 8'h3a: data <= "m"; + 8'h3b: data <= "j"; + 8'h3c: data <= "u"; + 8'h3d: data <= "7"; + 8'h3e: data <= "8"; + + 8'h41: data <= ","; + 8'h42: data <= "k"; + 8'h43: data <= "i"; + 8'h44: data <= "o"; + 8'h45: data <= "0"; + 8'h46: data <= "9"; + 8'h49: data <= "."; + 8'h4a: data <= "/"; + 8'h4b: data <= "l"; + 8'h4c: data <= ";"; + 8'h4d: data <= "p"; + 8'h4e: data <= "-"; + + 8'h52: data <= "'"; + 8'h54: data <= "["; + 8'h55: data <= "="; + 8'h58: data <= 0; /* Caps lock */ + 8'h59: data <= 0; /* Right shift */ + 8'h5a: data <= 8'h0d; /* Enter */ + 8'h5b: data <= "]"; + 8'h5d: data <= 8'h5c; /* "\\" */ + 8'h66: data <= 8'h08; /* BKSP */ + 8'h76: data <= 8'h1b; /* ESC */ + + 8'h8e: data <= "~"; + 8'h95: data <= "Q"; + 8'h96: data <= "!"; + 8'h9a: data <= "Z"; + 8'h9b: data <= "S"; + 8'h9c: data <= 8'h41 /* "A" */; + 8'h9d: data <= "W"; + 8'h9e: data <= "2"; + + 8'ha1: data <= "C"; + 8'ha2: data <= "X"; + 8'ha3: data <= "D"; + 8'ha4: data <= "E"; + 8'ha5: data <= "$"; + 8'ha6: data <= "#"; + 8'ha9: data <= " "; /* SPACE */ + 8'haa: data <= "V"; + 8'hab: data <= "F"; + 8'hac: data <= "T"; + 8'had: data <= "R"; + 8'hae: data <= "%"; + + 8'hb1: data <= "N"; + 8'hb2: data <= "B"; + 8'hb3: data <= "H"; + 8'hb4: data <= "G"; + 8'hb5: data <= "Y"; + 8'hb6: data <= "^"; + 8'hba: data <= "M"; + 8'hbb: data <= "J"; + 8'hbc: data <= "U"; + 8'hbd: data <= "&"; + 8'hbe: data <= "*"; + + 8'hc1: data <= "<"; + 8'hc2: data <= "K"; + 8'hc3: data <= "I"; + 8'hc4: data <= "O"; + 8'hc5: data <= ")"; + 8'hc6: data <= "("; + 8'hc9: data <= ">"; + 8'hca: data <= "?"; + 8'hcb: data <= "L"; + 8'hcc: data <= ":"; + 8'hcd: data <= "P"; + 8'hce: data <= "-"; + + 8'hd2: data <= "\""; + 8'hd4: data <= "{"; + 8'hd5: data <= "+"; + 8'hda: data <= 8'h0d; /* Enter */ + 8'hdb: data <= "}"; + 8'hdd: data <= "|"; + + 8'he6: data <= 8'h08; /* BKSP */ + + 8'hf6: data <= 8'h1b; /* ESC */ + default: data <= 0; /* All other keys are undefined */ + endcase +endmodule + diff --git a/v/vga.v b/v/vga.v new file mode 100644 index 0000000..bc7f74c --- /dev/null +++ b/v/vga.v @@ -0,0 +1,224 @@ +// vga.v + +// simple b&w 640x480 terminal with VGA output + +`include "vgacore.v" +`include "crt.v" +`include "video_ram.v" +`include "char_rom.v" +`include "ps2.v" +`include "scancode.v" + +module vga (reset_n, clock, + pixel, blank_n, + hsync, vsync, + ps2_clk, + ps2_data, + led_data); + + input reset_n; + input clock; + output [8:0] pixel; + output blank_n; + output hsync; + output vsync; + input ps2_clk, ps2_data; + output [7:0] led_data; + + wire startVGA; + wire resetVGA; + wire done; + wire clearing; + + wire [9:0] hloc; // horiz timing, including sync & blanking + wire [9:0] vloc; + + wire hblank; // set when in non-visable part of frame + wire vblank; + + wire [6:0] hpos; // horiz position 0..79, during visable frame + wire [8:0] vpos; // vert line 0..480, during visable frame + + reg [8:0] pixelData; + + wire [11:0] vpos_times_80; + + wire pixelclk; + reg [2:0] pclk; + + wire charclk; + reg charload; + reg [7:0] pixel_hold; + + reg crtclk; + wire ram_wclk; + wire ram_wslot; + + wire [11:0] ram_addr_write, ram_addr_video, ram_addr_mux; + wire [7:0] ram_data, ram_data_out, rom_data_out; + wire ram_we_n; + wire [9:0] rom_addr; + reg [7:0] rom_addr_char; + + char_rom char_rom(.addr(rom_addr), + .data(rom_data_out)); + + video_ram video_ram(.addr(ram_addr_mux), + .data_out(ram_data_out), .clk_r(charload/*charclk*/), + .data_in(ram_data), .clk_w(ram_wclk), .we_n(ram_we_n)); + + vgacore vgacore(.reset_n(reset_n), .clock(clock), + .hsync(hsync), + .vsync(vsync), + .hblank(hblank), + .vblank(vblank), + .enable(blank_n), + .hloc(hloc), + .vloc(vloc)); + + + wire kb_rdy; + wire kb_bsy; + wire kb_release; + wire [7:0] kb_scancode; + wire [7:0] kb_ascii; + wire kb_ascii_rdy; + reg [7:0] crt_data; + reg insert_crt_data; + + ps2 ps2(.clk(clock), + .rst_n(reset_n), + .ps2_clk(ps2_clk), + .ps2_data(ps2_data), + .scancode(kb_scancode), + .parity(), + .busy(kb_bsy), + .rdy(kb_rdy), + .error() + ); + + scancode_convert scancode_convert(.clock(clock), + .reset_n(reset_n), + .scancode(kb_scancode), + .ascii(kb_ascii), + .key_up(kb_release), + .strobe_in(kb_rdy), + .strobe_out(kb_ascii_rdy)); + + // debug - led output + assign led_data = { kb_ascii[6], kb_rdy, kb_ascii[5:0] }; + + always @(posedge clock or negedge reset_n) + if (~reset_n) + begin + crt_data <= 0; + insert_crt_data <= 0; + end + else + begin + if (kb_ascii_rdy) + begin + crt_data <= kb_ascii; + if (~kb_release) + insert_crt_data <= 1; + end + else + insert_crt_data <= 0; + end + +// assign insert_crt_data = kb_ascii_rdy && ~kb_release; + + wire [6:0] cursorh; + wire [5:0] cursorv; + wire cursor_match; + + crt crt(.reset_n(reset_n), + .clock(crtclk), + .insert(insert_crt_data), + .done(done), + .data(crt_data), + .clearing(clearing), + .ram_addr(ram_addr_write), + .ram_data(ram_data), + .ram_we_n(ram_we_n), + .ram_wclk(ram_wclk), + .ram_wslot(ram_wslot), + .cursorh(cursorh), + .cursorv(cursorv)); + + + // generate video ram address from (vpos/8) * 80 + hpos + assign vpos_times_80 = {vpos[8:3], 6'b0} + {2'b00, vpos[8:3], 4'b0}; + assign ram_addr_video = vpos_times_80 + {5'b00000, hpos}; + + assign ram_addr_mux = ram_we_n ? ram_addr_video : ram_addr_write; + + assign rom_addr = {rom_addr_char[6:0], vpos[2:0]}; + + assign cursor_match = (cursorh == hpos && cursorv == vpos[8:3]) ? 1 : 0; + + + // clock divider by 8 - assume 8x8 font + always @(posedge pixelclk or negedge reset_n) + if (~reset_n) + pclk = 3'b111; + else + pclk = (hblank || clearing) ? 3'b111 : pclk + 1; + + assign charclk = ~pclk[2]; + + // crtclk runs at half speed + always @(posedge pixelclk or negedge reset_n) + if (~reset_n) + crtclk = 1'b0; + else + crtclk = ~crtclk; + + // ram "writ slot" sits at 2nd half of charclk cycle + assign ram_wslot = ~pclk[1] & pclk[2]; + + // generate hpos, vpos (from hloc, vloc of vgacore) + assign hpos = hloc[9:3]; + assign vpos = vloc[8:0]; + + // latch ram output to form rom address + always @(posedge charclk) + rom_addr_char <= ram_data_out; + +/* +//temp-debug +//always @(posedge charclk) rom_addr_char <= {1'b0, hpos}; +wire [7:0] hack; +assign hack = {1'b0,hpos} + 8'h30; +//assign hack = {1'b0,hpos}; +always @(posedge charclk) rom_addr_char <= hack; +*/ + + // inhibit shift and load instead during last slot + always @(negedge pixelclk or negedge reset_n) + if (~reset_n) + charload = 0; + else + charload = pclk == 3'b111 ? 1 : 0; + + // shift pixel data, one bit at a time (or load when at last slot) + always @(posedge pixelclk) + pixel_hold <= charload ? + (cursor_match ? 8'hff : rom_data_out) : + { pixel_hold[6:0], 1'b0 }; + + // clock pixel data on pixel clock + always @(posedge pixelclk) + pixelData <= pixel_hold[7] ? 9'h1ff : 9'h000; + + assign pixel = (hblank || vblank) ? 9'h000 : pixelData; + + // reset & start VGA + assign startVGA = 1; + assign resetVGA = reset_n & startVGA; + + // Provide 25MHz pixel clock + assign pixelclk = clock; + +endmodule + diff --git a/v/vgacore.v b/v/vgacore.v new file mode 100644 index 0000000..a9a41af --- /dev/null +++ b/v/vgacore.v @@ -0,0 +1,153 @@ +// vgacore.v +// +// Creates VGA timing signals to a monitor, currently for 60Hz @ 640 * 480 +// +// To change the resolution or refresh rate, change the value of +// the constants and the generics to whatever is desired. Changing the +// resolution and/or refresh also means the clock speed may have to change, +// currently based off a 25MHz clock... +// + +module vgacore(reset_n, clock, + hblank, vblank, + hsync, vsync, + enable, + hloc, vloc); + + input reset_n; + input clock; + + output hblank; + output vblank; + + output hsync; + output vsync; + + output enable; + + reg hblank; + reg hsync, vsync; + reg enable; + + output [9:0] hloc; + output [9:0] vloc; + + parameter H_SIZE = 640; + parameter V_SIZE = 480; + + // sync signals + // + // |<--- Active Region --->|<--------- Blanking Region ------->| + // | (Pixels) | | + // | | | + // | | | + // -----+---------- ... --------+------------- ------------+--- + // | | | | | | + // | | |<--Front |<--Sync |<--Back | + // | | | Porch-->| Time-->| Porch-->| + //-- | | ------------ | + // | | | + // |<---------------------------- Period --------------------->| + // + + // horizontal timing signals +`define H_PIXELS H_SIZE +`define H_FRONTPORCH 30 + (640 - H_SIZE) / 2 +`define H_SYNCTIME 100 +`define H_BACKPORCH 30 + (640 - H_SIZE) / 2 +`define H_PERIOD `H_PIXELS+ `H_FRONTPORCH+ `H_SYNCTIME+ `H_BACKPORCH + + // vertical timing signals +`define V_LINES V_SIZE +`define V_FRONTPORCH 10 + (480 - V_SIZE) / 2 +`define V_SYNCTIME 2 +`define V_BACKPORCH 32 + (480 - V_SIZE) / 2 +`define V_PERIOD `V_SYNCTIME+ `V_LINES+ `V_FRONTPORCH+ `V_BACKPORCH + + + reg [10:0] hcnt; // horizontal pixel counter + reg [9:0] vcnt; // vertical line counter + + // control the reset, increment and overflow of the horiz pixel count + always @(posedge clock or negedge reset_n) + // reset asynchronously clears horizontal counter + if (~reset_n) + hcnt <= 0; + else + // horiz. counter increments on rising edge of dot clock + // horiz. counter restarts after the horizontal period + if (hcnt < (`H_PERIOD - 1)) + hcnt <= hcnt + 1; + else + hcnt <= 0; + + + // control the reset, increment and overflow of the vert line ctr, + // after every horiz line +// always @(negedge hsync or negedge reset_n) + always @(negedge hblank or negedge reset_n) + // reset asynchronously clears line counter + if (~reset_n) + vcnt <= 0; + else + // vert. line counter increments after every horiz. line + // vert. line counter rolls-over after max lines + if (vcnt < (`V_PERIOD - 1)) + vcnt <= vcnt + 1; + else + vcnt <= 0; + + + // set the horizontal sync high time and low time + always @(posedge clock or negedge reset_n) + // reset asynchronously sets horizontal sync to inactive + if (~reset_n) + hsync <= 0; + else + // horizontal sync is recomputed on the rising edge of every dot clk + // horiz. sync is low to signal start of a new line + if (hcnt >= (`H_FRONTPORCH + `H_PIXELS) && + hcnt < (`H_FRONTPORCH + `H_PIXELS + `H_SYNCTIME)) + hsync <= 1; + else + hsync <= 0; + + // set the vertical sync high time and low time + always @(posedge clock or negedge reset_n) + // reset asynchronously sets vertical sync to inactive + if (~reset_n) + vsync <= 0; + else + // vertical sync is recomputed at the end of every line of pixels + // vert. sync is low to signal start of a new frame + if (vcnt >= (`V_LINES + `V_FRONTPORCH) && + vcnt < (`V_LINES + `V_FRONTPORCH + `V_SYNCTIME)) + vsync <= 1; + else + vsync <= 0; + + // blanking +// assign hblank = (hcnt >= `H_PIXELS) && (hcnt < `H_PERIOD); + assign vblank = (vcnt >= `V_LINES) && (vcnt < `V_PERIOD); + + always @(posedge clock or negedge reset_n) + if (~reset_n) + hblank <= 0; + else + if (hcnt < `H_PIXELS) + hblank <= 0; + else + hblank <= 1; + + // asserts the blanking signal (active low) + always @(posedge clock) + // if we are outside the visible range on the screen then blank + if (hcnt >= `H_PIXELS || vcnt >= `V_LINES) + enable <= 0; + else + enable <= 1; + + assign hloc = hcnt[9:0]; + assign vloc = vcnt[9:0]; + +endmodule diff --git a/v/video_mem.v b/v/video_mem.v new file mode 100644 index 0000000..ee34786 --- /dev/null +++ b/v/video_mem.v @@ -0,0 +1,23 @@ +// video_mem.v + +/* 1kx32 static ram */ + +module video_ram(addr, data_in, data_out, ce_n, we_n); + + input [10:0] addr; + input [7:0] data_in; + input ce_n, we_n; + output [7:0] data_out; + + reg [7:0] ram [0:2047]; + + always @(negedge we_n) + begin + if (ce_n == 0) + ram[addr] = data_in; + end + + assign data_out = ram[addr]; +endmodule + + diff --git a/v/video_ram.v b/v/video_ram.v new file mode 100644 index 0000000..563498b --- /dev/null +++ b/v/video_ram.v @@ -0,0 +1,32 @@ +// video_mem.v + +/* 2kx8 static sync ram */ + +module video_ram(addr, data_in, data_out, clk_r, clk_w, we_n); + + input [11:0] addr; + input [7:0] data_in; + input clk_r, clk_w, we_n; + output [7:0] data_out; + + reg [7:0] ram [0:2048]; + reg [11:0] ram_addr_w; + reg [11:0] ram_addr_r; + + always @(posedge clk_w) + begin + ram_addr_w <= addr; + if (we_n == 0) + ram[ram_addr_w] <= data_in; + end + + always @(posedge clk_r) + begin + ram_addr_r <= addr; + end + + assign data_out = ram[ram_addr_r]; + +// assign data_out = 8'h20 + addr[7:0]; + +endmodule diff --git a/v/xsa-200.ucf b/v/xsa-200.ucf new file mode 100644 index 0000000..aea988b --- /dev/null +++ b/v/xsa-200.ucf @@ -0,0 +1,17 @@ +net clka loc=r8; +net clkb loc=b8; + +net ps2_clk loc=f4; +net ps2_data loc=e1; + +net vga_blue0 loc=h4; +net vga_blue1 loc=k3; +net vga_blue2 loc=l5; +net vga_green0 loc=h2; +net vga_green1 loc=k5; +net vga_green2 loc=r1; +net vga_red0 loc=j1; +net vga_red1 loc=m1; +net vga_red2 loc=t2; +net vga_hsync_n loc=k4; +net vga_vsync_n loc=k1;