From 6f6a0c3344ea905979901065cf723bdf168c1bf1 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 18 Oct 2013 11:03:26 +0100 Subject: start --- quartus/CII_Starter_pin_assignments.csv | 455 +++++++++ quartus/README | 6 + quartus/T65/T65.vhd | 564 ++++++++++++ quartus/T65/T65_ALU.vhd | 260 ++++++ quartus/T65/T65_MCode.vhd | 1052 +++++++++++++++++++++ quartus/T65/T65_Pack.vhd | 117 +++ quartus/bbc_micro.qpf | 30 + quartus/bbc_micro_de1.qsf | 537 +++++++++++ quartus/bbc_micro_de1.vhd | 1265 ++++++++++++++++++++++++++ quartus/bbc_micro_de1_tb.vhd | 299 ++++++ quartus/debugger.vhd | 300 ++++++ quartus/i2c_loader.vhd | 304 +++++++ quartus/i2s_intf.vhd | 195 ++++ quartus/keyboard.odg | Bin 0 -> 16836 bytes quartus/keyboard.png | Bin 0 -> 60246 bytes quartus/keyboard.txt | 81 ++ quartus/keyboard.vhd | 294 ++++++ quartus/m6522.vhd | 886 ++++++++++++++++++ quartus/m6522_tb.vhd | 378 ++++++++ quartus/mc6845.vhd | 481 ++++++++++ quartus/pll32.ppf | 11 + quartus/pll32.qip | 4 + quartus/pll32.vhd | 365 ++++++++ quartus/ps2_intf.vhd | 157 ++++ quartus/roms/saa5050/genrom.c | 180 ++++ quartus/roms/saa5050/saa5050.hex | 257 ++++++ quartus/roms/saa5050/saa5050.rom | Bin 0 -> 4096 bytes quartus/saa5050.vhd | 391 ++++++++ quartus/saa5050_rom.qip | 3 + quartus/saa5050_rom.vhd | 168 ++++ quartus/seg7.vhd | 68 ++ quartus/sn76489-1.0/COPYING | 340 +++++++ quartus/sn76489-1.0/README | 143 +++ quartus/sn76489-1.0/sn76489_attenuator-c.vhd | 14 + quartus/sn76489-1.0/sn76489_attenuator.vhd | 114 +++ quartus/sn76489-1.0/sn76489_clock_div-c.vhd | 14 + quartus/sn76489-1.0/sn76489_clock_div.vhd | 134 +++ quartus/sn76489-1.0/sn76489_comp_pack-p.vhd | 96 ++ quartus/sn76489-1.0/sn76489_latch_ctrl-c.vhd | 14 + quartus/sn76489-1.0/sn76489_latch_ctrl.vhd | 138 +++ quartus/sn76489-1.0/sn76489_noise-c.vhd | 19 + quartus/sn76489-1.0/sn76489_noise.vhd | 281 ++++++ quartus/sn76489-1.0/sn76489_tone-c.vhd | 19 + quartus/sn76489-1.0/sn76489_tone.vhd | 191 ++++ quartus/sn76489-1.0/sn76489_top-c.vhd | 31 + quartus/sn76489-1.0/sn76489_top.vhd | 210 +++++ quartus/vidproc.vhd | 279 ++++++ 47 files changed, 11145 insertions(+) create mode 100644 quartus/CII_Starter_pin_assignments.csv create mode 100644 quartus/README create mode 100644 quartus/T65/T65.vhd create mode 100644 quartus/T65/T65_ALU.vhd create mode 100644 quartus/T65/T65_MCode.vhd create mode 100644 quartus/T65/T65_Pack.vhd create mode 100644 quartus/bbc_micro.qpf create mode 100644 quartus/bbc_micro_de1.qsf create mode 100644 quartus/bbc_micro_de1.vhd create mode 100644 quartus/bbc_micro_de1_tb.vhd create mode 100644 quartus/debugger.vhd create mode 100644 quartus/i2c_loader.vhd create mode 100644 quartus/i2s_intf.vhd create mode 100644 quartus/keyboard.odg create mode 100644 quartus/keyboard.png create mode 100644 quartus/keyboard.txt create mode 100644 quartus/keyboard.vhd create mode 100644 quartus/m6522.vhd create mode 100644 quartus/m6522_tb.vhd create mode 100644 quartus/mc6845.vhd create mode 100644 quartus/pll32.ppf create mode 100644 quartus/pll32.qip create mode 100644 quartus/pll32.vhd create mode 100644 quartus/ps2_intf.vhd create mode 100644 quartus/roms/saa5050/genrom.c create mode 100644 quartus/roms/saa5050/saa5050.hex create mode 100644 quartus/roms/saa5050/saa5050.rom create mode 100644 quartus/saa5050.vhd create mode 100644 quartus/saa5050_rom.qip create mode 100644 quartus/saa5050_rom.vhd create mode 100644 quartus/seg7.vhd create mode 100644 quartus/sn76489-1.0/COPYING create mode 100644 quartus/sn76489-1.0/README create mode 100644 quartus/sn76489-1.0/sn76489_attenuator-c.vhd create mode 100644 quartus/sn76489-1.0/sn76489_attenuator.vhd create mode 100644 quartus/sn76489-1.0/sn76489_clock_div-c.vhd create mode 100644 quartus/sn76489-1.0/sn76489_clock_div.vhd create mode 100644 quartus/sn76489-1.0/sn76489_comp_pack-p.vhd create mode 100644 quartus/sn76489-1.0/sn76489_latch_ctrl-c.vhd create mode 100644 quartus/sn76489-1.0/sn76489_latch_ctrl.vhd create mode 100644 quartus/sn76489-1.0/sn76489_noise-c.vhd create mode 100644 quartus/sn76489-1.0/sn76489_noise.vhd create mode 100644 quartus/sn76489-1.0/sn76489_tone-c.vhd create mode 100644 quartus/sn76489-1.0/sn76489_tone.vhd create mode 100644 quartus/sn76489-1.0/sn76489_top-c.vhd create mode 100644 quartus/sn76489-1.0/sn76489_top.vhd create mode 100644 quartus/vidproc.vhd diff --git a/quartus/CII_Starter_pin_assignments.csv b/quartus/CII_Starter_pin_assignments.csv new file mode 100644 index 0000000..f46adb0 --- /dev/null +++ b/quartus/CII_Starter_pin_assignments.csv @@ -0,0 +1,455 @@ + +# Note: The column header names should not be changed if you wish to import this .csv file into the Quartus II software. + +From,To,Assignment Name,Value,Enabled +,GPIO_0[0],Location,PIN_A13,Yes +,GPIO_0[1],Location,PIN_B13,Yes +,GPIO_0[2],Location,PIN_A14,Yes +,GPIO_0[3],Location,PIN_B14,Yes +,GPIO_0[4],Location,PIN_A15,Yes +,GPIO_0[5],Location,PIN_B15,Yes +,GPIO_0[6],Location,PIN_A16,Yes +,GPIO_0[7],Location,PIN_B16,Yes +,GPIO_0[8],Location,PIN_A17,Yes +,GPIO_0[9],Location,PIN_B17,Yes +,GPIO_0[10],Location,PIN_A18,Yes +,GPIO_0[11],Location,PIN_B18,Yes +,GPIO_0[12],Location,PIN_A19,Yes +,GPIO_0[13],Location,PIN_B19,Yes +,GPIO_0[14],Location,PIN_A20,Yes +,GPIO_0[15],Location,PIN_B20,Yes +,GPIO_0[16],Location,PIN_C21,Yes +,GPIO_0[17],Location,PIN_C22,Yes +,GPIO_0[18],Location,PIN_D21,Yes +,GPIO_0[19],Location,PIN_D22,Yes +,GPIO_0[20],Location,PIN_E21,Yes +,GPIO_0[21],Location,PIN_E22,Yes +,GPIO_0[22],Location,PIN_F21,Yes +,GPIO_0[23],Location,PIN_F22,Yes +,GPIO_0[24],Location,PIN_G21,Yes +,GPIO_0[25],Location,PIN_G22,Yes +,GPIO_0[26],Location,PIN_J21,Yes +,GPIO_0[27],Location,PIN_J22,Yes +,GPIO_0[28],Location,PIN_K21,Yes +,GPIO_0[29],Location,PIN_K22,Yes +,GPIO_0[30],Location,PIN_J19,Yes +,GPIO_0[31],Location,PIN_J20,Yes +,GPIO_0[32],Location,PIN_J18,Yes +,GPIO_0[33],Location,PIN_K20,Yes +,GPIO_0[34],Location,PIN_L19,Yes +,GPIO_0[35],Location,PIN_L18,Yes +,GPIO_1[0],Location,PIN_H12,Yes +,GPIO_1[1],Location,PIN_H13,Yes +,GPIO_1[2],Location,PIN_H14,Yes +,GPIO_1[3],Location,PIN_G15,Yes +,GPIO_1[4],Location,PIN_E14,Yes +,GPIO_1[5],Location,PIN_E15,Yes +,GPIO_1[6],Location,PIN_F15,Yes +,GPIO_1[7],Location,PIN_G16,Yes +,GPIO_1[8],Location,PIN_F12,Yes +,GPIO_1[9],Location,PIN_F13,Yes +,GPIO_1[10],Location,PIN_C14,Yes +,GPIO_1[11],Location,PIN_D14,Yes +,GPIO_1[12],Location,PIN_D15,Yes +,GPIO_1[13],Location,PIN_D16,Yes +,GPIO_1[14],Location,PIN_C17,Yes +,GPIO_1[15],Location,PIN_C18,Yes +,GPIO_1[16],Location,PIN_C19,Yes +,GPIO_1[17],Location,PIN_C20,Yes +,GPIO_1[18],Location,PIN_D19,Yes +,GPIO_1[19],Location,PIN_D20,Yes +,GPIO_1[20],Location,PIN_E20,Yes +,GPIO_1[21],Location,PIN_F20,Yes +,GPIO_1[22],Location,PIN_E19,Yes +,GPIO_1[23],Location,PIN_E18,Yes +,GPIO_1[24],Location,PIN_G20,Yes +,GPIO_1[25],Location,PIN_G18,Yes +,GPIO_1[26],Location,PIN_G17,Yes +,GPIO_1[27],Location,PIN_H17,Yes +,GPIO_1[28],Location,PIN_J15,Yes +,GPIO_1[29],Location,PIN_H18,Yes +,GPIO_1[30],Location,PIN_N22,Yes +,GPIO_1[31],Location,PIN_N21,Yes +,GPIO_1[32],Location,PIN_P15,Yes +,GPIO_1[33],Location,PIN_N15,Yes +,GPIO_1[34],Location,PIN_P17,Yes +,GPIO_1[35],Location,PIN_P18,Yes +,GPIO_0[0],I/O Standard,LVTTL,Yes +,GPIO_0[1],I/O Standard,LVTTL,Yes +,GPIO_0[2],I/O Standard,LVTTL,Yes +,GPIO_0[3],I/O Standard,LVTTL,Yes +,GPIO_0[4],I/O Standard,LVTTL,Yes +,GPIO_0[5],I/O Standard,LVTTL,Yes +,GPIO_0[6],I/O Standard,LVTTL,Yes +,GPIO_0[7],I/O Standard,LVTTL,Yes +,GPIO_0[8],I/O Standard,LVTTL,Yes +,GPIO_0[9],I/O Standard,LVTTL,Yes +,GPIO_0[10],I/O Standard,LVTTL,Yes +,GPIO_0[11],I/O Standard,LVTTL,Yes +,GPIO_0[12],I/O Standard,LVTTL,Yes +,GPIO_0[13],I/O Standard,LVTTL,Yes +,GPIO_0[14],I/O Standard,LVTTL,Yes +,GPIO_0[15],I/O Standard,LVTTL,Yes +,GPIO_0[16],I/O Standard,LVTTL,Yes +,GPIO_0[17],I/O Standard,LVTTL,Yes +,GPIO_0[18],I/O Standard,LVTTL,Yes +,GPIO_0[19],I/O Standard,LVTTL,Yes +,GPIO_0[20],I/O Standard,LVTTL,Yes +,GPIO_0[21],I/O Standard,LVTTL,Yes +,GPIO_0[22],I/O Standard,LVTTL,Yes +,GPIO_0[23],I/O Standard,LVTTL,Yes +,GPIO_0[24],I/O Standard,LVTTL,Yes +,GPIO_0[25],I/O Standard,LVTTL,Yes +,GPIO_0[26],I/O Standard,LVTTL,Yes +,GPIO_0[27],I/O Standard,LVTTL,Yes +,GPIO_0[28],I/O Standard,LVTTL,Yes +,GPIO_0[29],I/O Standard,LVTTL,Yes +,GPIO_0[30],I/O Standard,LVTTL,Yes +,GPIO_0[31],I/O Standard,LVTTL,Yes +,GPIO_0[32],I/O Standard,LVTTL,Yes +,GPIO_0[33],I/O Standard,LVTTL,Yes +,GPIO_0[34],I/O Standard,LVTTL,Yes +,GPIO_0[35],I/O Standard,LVTTL,Yes +,GPIO_1[0],I/O Standard,LVTTL,Yes +,GPIO_1[1],I/O Standard,LVTTL,Yes +,GPIO_1[2],I/O Standard,LVTTL,Yes +,GPIO_1[3],I/O Standard,LVTTL,Yes +,GPIO_1[4],I/O Standard,LVTTL,Yes +,GPIO_1[5],I/O Standard,LVTTL,Yes +,GPIO_1[6],I/O Standard,LVTTL,Yes +,GPIO_1[7],I/O Standard,LVTTL,Yes +,GPIO_1[8],I/O Standard,LVTTL,Yes +,GPIO_1[9],I/O Standard,LVTTL,Yes +,GPIO_1[10],I/O Standard,LVTTL,Yes +,GPIO_1[11],I/O Standard,LVTTL,Yes +,GPIO_1[12],I/O Standard,LVTTL,Yes +,GPIO_1[13],I/O Standard,LVTTL,Yes +,GPIO_1[14],I/O Standard,LVTTL,Yes +,GPIO_1[15],I/O Standard,LVTTL,Yes +,GPIO_1[16],I/O Standard,LVTTL,Yes +,GPIO_1[17],I/O Standard,LVTTL,Yes +,GPIO_1[18],I/O Standard,LVTTL,Yes +,GPIO_1[19],I/O Standard,LVTTL,Yes +,GPIO_1[20],I/O Standard,LVTTL,Yes +,GPIO_1[21],I/O Standard,LVTTL,Yes +,GPIO_1[22],I/O Standard,LVTTL,Yes +,GPIO_1[23],I/O Standard,LVTTL,Yes +,GPIO_1[24],I/O Standard,LVTTL,Yes +,GPIO_1[25],I/O Standard,LVTTL,Yes +,GPIO_1[26],I/O Standard,LVTTL,Yes +,GPIO_1[27],I/O Standard,LVTTL,Yes +,GPIO_1[28],I/O Standard,LVTTL,Yes +,GPIO_1[29],I/O Standard,LVTTL,Yes +,GPIO_1[30],I/O Standard,LVTTL,Yes +,GPIO_1[31],I/O Standard,LVTTL,Yes +,GPIO_1[32],I/O Standard,LVTTL,Yes +,GPIO_1[33],I/O Standard,LVTTL,Yes +,GPIO_1[34],I/O Standard,LVTTL,Yes +,GPIO_1[35],I/O Standard,LVTTL,Yes +,SW[0],Location,PIN_L22,Yes +,SW[1],Location,PIN_L21,Yes +,SW[2],Location,PIN_M22,Yes +,SW[3],Location,PIN_V12,Yes +,SW[4],Location,PIN_W12,Yes +,SW[5],Location,PIN_U12,Yes +,SW[6],Location,PIN_U11,Yes +,SW[7],Location,PIN_M2,Yes +,SW[8],Location,PIN_M1,Yes +,SW[9],Location,PIN_L2,Yes +,SW[0],I/O Standard,LVTTL,Yes +,SW[1],I/O Standard,LVTTL,Yes +,SW[2],I/O Standard,LVTTL,Yes +,SW[3],I/O Standard,LVTTL,Yes +,SW[4],I/O Standard,LVTTL,Yes +,SW[5],I/O Standard,LVTTL,Yes +,SW[6],I/O Standard,LVTTL,Yes +,SW[7],I/O Standard,LVTTL,Yes +,SW[8],I/O Standard,LVTTL,Yes +,SW[9],I/O Standard,LVTTL,Yes +,HEX0[0],Location,PIN_J2,Yes +,HEX0[1],Location,PIN_J1,Yes +,HEX0[2],Location,PIN_H2,Yes +,HEX0[3],Location,PIN_H1,Yes +,HEX0[4],Location,PIN_F2,Yes +,HEX0[5],Location,PIN_F1,Yes +,HEX0[6],Location,PIN_E2,Yes +,HEX1[0],Location,PIN_E1,Yes +,HEX1[1],Location,PIN_H6,Yes +,HEX1[2],Location,PIN_H5,Yes +,HEX1[3],Location,PIN_H4,Yes +,HEX1[4],Location,PIN_G3,Yes +,HEX1[5],Location,PIN_D2,Yes +,HEX1[6],Location,PIN_D1,Yes +,HEX2[0],Location,PIN_G5,Yes +,HEX2[1],Location,PIN_G6,Yes +,HEX2[2],Location,PIN_C2,Yes +,HEX2[3],Location,PIN_C1,Yes +,HEX2[4],Location,PIN_E3,Yes +,HEX2[5],Location,PIN_E4,Yes +,HEX2[6],Location,PIN_D3,Yes +,HEX3[0],Location,PIN_F4,Yes +,HEX3[1],Location,PIN_D5,Yes +,HEX3[2],Location,PIN_D6,Yes +,HEX3[3],Location,PIN_J4,Yes +,HEX3[4],Location,PIN_L8,Yes +,HEX3[5],Location,PIN_F3,Yes +,HEX3[6],Location,PIN_D4,Yes +,HEX0[0],I/O Standard,LVTTL,Yes +,HEX0[1],I/O Standard,LVTTL,Yes +,HEX0[2],I/O Standard,LVTTL,Yes +,HEX0[3],I/O Standard,LVTTL,Yes +,HEX0[4],I/O Standard,LVTTL,Yes +,HEX0[5],I/O Standard,LVTTL,Yes +,HEX0[6],I/O Standard,LVTTL,Yes +,HEX1[0],I/O Standard,LVTTL,Yes +,HEX1[1],I/O Standard,LVTTL,Yes +,HEX1[2],I/O Standard,LVTTL,Yes +,HEX1[3],I/O Standard,LVTTL,Yes +,HEX1[4],I/O Standard,LVTTL,Yes +,HEX1[5],I/O Standard,LVTTL,Yes +,HEX1[6],I/O Standard,LVTTL,Yes +,HEX2[0],I/O Standard,LVTTL,Yes +,HEX2[1],I/O Standard,LVTTL,Yes +,HEX2[2],I/O Standard,LVTTL,Yes +,HEX2[3],I/O Standard,LVTTL,Yes +,HEX2[4],I/O Standard,LVTTL,Yes +,HEX2[5],I/O Standard,LVTTL,Yes +,HEX2[6],I/O Standard,LVTTL,Yes +,HEX3[0],I/O Standard,LVTTL,Yes +,HEX3[1],I/O Standard,LVTTL,Yes +,HEX3[2],I/O Standard,LVTTL,Yes +,HEX3[3],I/O Standard,LVTTL,Yes +,HEX3[4],I/O Standard,LVTTL,Yes +,HEX3[5],I/O Standard,LVTTL,Yes +,HEX3[6],I/O Standard,LVTTL,Yes +,KEY[0],Location,PIN_R22,Yes +,KEY[1],Location,PIN_R21,Yes +,KEY[2],Location,PIN_T22,Yes +,KEY[3],Location,PIN_T21,Yes +,LEDR[0],Location,PIN_R20,Yes +,LEDR[1],Location,PIN_R19,Yes +,LEDR[2],Location,PIN_U19,Yes +,LEDR[3],Location,PIN_Y19,Yes +,LEDR[4],Location,PIN_T18,Yes +,LEDR[5],Location,PIN_V19,Yes +,LEDR[6],Location,PIN_Y18,Yes +,LEDR[7],Location,PIN_U18,Yes +,LEDR[8],Location,PIN_R18,Yes +,LEDR[9],Location,PIN_R17,Yes +,LEDG[0],Location,PIN_U22,Yes +,LEDG[1],Location,PIN_U21,Yes +,LEDG[2],Location,PIN_V22,Yes +,LEDG[3],Location,PIN_V21,Yes +,LEDG[4],Location,PIN_W22,Yes +,LEDG[5],Location,PIN_W21,Yes +,LEDG[6],Location,PIN_Y22,Yes +,LEDG[7],Location,PIN_Y21,Yes +,KEY[0],I/O Standard,LVTTL,Yes +,KEY[1],I/O Standard,LVTTL,Yes +,KEY[2],I/O Standard,LVTTL,Yes +,KEY[3],I/O Standard,LVTTL,Yes +,LEDR[0],I/O Standard,LVTTL,Yes +,LEDR[1],I/O Standard,LVTTL,Yes +,LEDR[2],I/O Standard,LVTTL,Yes +,LEDR[3],I/O Standard,LVTTL,Yes +,LEDR[4],I/O Standard,LVTTL,Yes +,LEDR[5],I/O Standard,LVTTL,Yes +,LEDR[6],I/O Standard,LVTTL,Yes +,LEDR[7],I/O Standard,LVTTL,Yes +,LEDR[8],I/O Standard,LVTTL,Yes +,LEDR[9],I/O Standard,LVTTL,Yes +,LEDG[0],I/O Standard,LVTTL,Yes +,LEDG[1],I/O Standard,LVTTL,Yes +,LEDG[2],I/O Standard,LVTTL,Yes +,LEDG[3],I/O Standard,LVTTL,Yes +,LEDG[4],I/O Standard,LVTTL,Yes +,LEDG[5],I/O Standard,LVTTL,Yes +,LEDG[6],I/O Standard,LVTTL,Yes +,LEDG[7],I/O Standard,LVTTL,Yes +,CLOCK_27[0],Location,PIN_D12,Yes +,CLOCK_27[1],Location,PIN_E12,Yes +,CLOCK_24[0],Location,PIN_B12,Yes +,CLOCK_24[1],Location,PIN_A12,Yes +,CLOCK_50,Location,PIN_L1,Yes +,EXT_CLOCK,Location,PIN_M21,Yes +,CLOCK_27[1],I/O Standard,LVTTL,Yes +,CLOCK_24[0],I/O Standard,LVTTL,Yes +,CLOCK_24[1],I/O Standard,LVTTL,Yes +,CLOCK_50,I/O Standard,LVTTL,Yes +,EXT_CLOCK,I/O Standard,LVTTL,Yes +,PS2_CLK,Location,PIN_H15,Yes +,PS2_DAT,Location,PIN_J14,Yes +,UART_RXD,Location,PIN_F14,Yes +,UART_TXD,Location,PIN_G12,Yes +,PS2_CLK,I/O Standard,LVTTL,Yes +,PS2_DAT,I/O Standard,LVTTL,Yes +,UART_RXD,I/O Standard,LVTTL,Yes +,UART_TXD,I/O Standard,LVTTL,Yes +,TDI,Location,PIN_E8,Yes +,TCS,Location,PIN_D8,Yes +,TCK,Location,PIN_C7,Yes +,TDO,Location,PIN_D7,Yes +,TDI,I/O Standard,LVTTL,Yes +,TCS,I/O Standard,LVTTL,Yes +,TCK,I/O Standard,LVTTL,Yes +,TDO,I/O Standard,LVTTL,Yes +,VGA_R[0],Location,PIN_D9,Yes +,VGA_R[1],Location,PIN_C9,Yes +,VGA_R[2],Location,PIN_A7,Yes +,VGA_R[3],Location,PIN_B7,Yes +,VGA_G[0],Location,PIN_B8,Yes +,VGA_G[1],Location,PIN_C10,Yes +,VGA_G[2],Location,PIN_B9,Yes +,VGA_G[3],Location,PIN_A8,Yes +,VGA_B[0],Location,PIN_A9,Yes +,VGA_B[1],Location,PIN_D11,Yes +,VGA_B[2],Location,PIN_A10,Yes +,VGA_B[3],Location,PIN_B10,Yes +,VGA_HS,Location,PIN_A11,Yes +,VGA_VS,Location,PIN_B11,Yes +,VGA_R[0],I/O Standard,LVTTL,Yes +,VGA_R[1],I/O Standard,LVTTL,Yes +,VGA_R[2],I/O Standard,LVTTL,Yes +,VGA_R[3],I/O Standard,LVTTL,Yes +,VGA_G[0],I/O Standard,LVTTL,Yes +,VGA_G[1],I/O Standard,LVTTL,Yes +,VGA_G[2],I/O Standard,LVTTL,Yes +,VGA_G[3],I/O Standard,LVTTL,Yes +,VGA_B[0],I/O Standard,LVTTL,Yes +,VGA_B[1],I/O Standard,LVTTL,Yes +,VGA_B[2],I/O Standard,LVTTL,Yes +,VGA_B[3],I/O Standard,LVTTL,Yes +,VGA_HS,I/O Standard,LVTTL,Yes +,VGA_VS,I/O Standard,LVTTL,Yes +,I2C_SCLK,Location,PIN_A3,Yes +,I2C_SDAT,Location,PIN_B3,Yes +,AUD_ADCLRCK,Location,PIN_A6,Yes +,AUD_ADCDAT,Location,PIN_B6,Yes +,AUD_DACLRCK,Location,PIN_A5,Yes +,AUD_DACDAT,Location,PIN_B5,Yes +,AUD_XCK,Location,PIN_B4,Yes +,AUD_BCLK,Location,PIN_A4,Yes +,I2C_SCLK,I/O Standard,LVTTL,Yes +,I2C_SDAT,I/O Standard,LVTTL,Yes +,AUD_ADCLRCK,I/O Standard,LVTTL,Yes +,AUD_ADCDAT,I/O Standard,LVTTL,Yes +,AUD_DACLRCK,I/O Standard,LVTTL,Yes +,AUD_DACDAT,I/O Standard,LVTTL,Yes +,AUD_XCK,I/O Standard,LVTTL,Yes +,AUD_BCLK,I/O Standard,LVTTL,Yes +,DRAM_ADDR[0],Location,PIN_W4,Yes +,DRAM_ADDR[1],Location,PIN_W5,Yes +,DRAM_ADDR[2],Location,PIN_Y3,Yes +,DRAM_ADDR[3],Location,PIN_Y4,Yes +,DRAM_ADDR[4],Location,PIN_R6,Yes +,DRAM_ADDR[5],Location,PIN_R5,Yes +,DRAM_ADDR[6],Location,PIN_P6,Yes +,DRAM_ADDR[7],Location,PIN_P5,Yes +,DRAM_ADDR[8],Location,PIN_P3,Yes +,DRAM_ADDR[9],Location,PIN_N4,Yes +,DRAM_ADDR[10],Location,PIN_W3,Yes +,DRAM_ADDR[11],Location,PIN_N6,Yes +,DRAM_BA_0,Location,PIN_U3,Yes +,DRAM_BA_1,Location,PIN_V4,Yes +,DRAM_CAS_N,Location,PIN_T3,Yes +,DRAM_CKE,Location,PIN_N3,Yes +,DRAM_CLK,Location,PIN_U4,Yes +,DRAM_CS_N,Location,PIN_T6,Yes +,DRAM_DQ[0],Location,PIN_U1,Yes +,DRAM_DQ[1],Location,PIN_U2,Yes +,DRAM_DQ[2],Location,PIN_V1,Yes +,DRAM_DQ[3],Location,PIN_V2,Yes +,DRAM_DQ[4],Location,PIN_W1,Yes +,DRAM_DQ[5],Location,PIN_W2,Yes +,DRAM_DQ[6],Location,PIN_Y1,Yes +,DRAM_DQ[7],Location,PIN_Y2,Yes +,DRAM_DQ[8],Location,PIN_N1,Yes +,DRAM_DQ[9],Location,PIN_N2,Yes +,DRAM_DQ[10],Location,PIN_P1,Yes +,DRAM_DQ[11],Location,PIN_P2,Yes +,DRAM_DQ[12],Location,PIN_R1,Yes +,DRAM_DQ[13],Location,PIN_R2,Yes +,DRAM_DQ[14],Location,PIN_T1,Yes +,DRAM_DQ[15],Location,PIN_T2,Yes +,DRAM_LDQM,Location,PIN_R7,Yes +,DRAM_RAS_N,Location,PIN_T5,Yes +,DRAM_UDQM,Location,PIN_M5,Yes +,DRAM_WE_N,Location,PIN_R8,Yes +,FL_ADDR[0],Location,PIN_AB20,Yes +,FL_ADDR[1],Location,PIN_AA14,Yes +,FL_ADDR[2],Location,PIN_Y16,Yes +,FL_ADDR[3],Location,PIN_R15,Yes +,FL_ADDR[4],Location,PIN_T15,Yes +,FL_ADDR[5],Location,PIN_U15,Yes +,FL_ADDR[6],Location,PIN_V15,Yes +,FL_ADDR[7],Location,PIN_W15,Yes +,FL_ADDR[8],Location,PIN_R14,Yes +,FL_ADDR[9],Location,PIN_Y13,Yes +,FL_ADDR[10],Location,PIN_R12,Yes +,FL_ADDR[11],Location,PIN_T12,Yes +,FL_ADDR[12],Location,PIN_AB14,Yes +,FL_ADDR[13],Location,PIN_AA13,Yes +,FL_ADDR[14],Location,PIN_AB13,Yes +,FL_ADDR[15],Location,PIN_AA12,Yes +,FL_ADDR[16],Location,PIN_AB12,Yes +,FL_ADDR[17],Location,PIN_AA20,Yes +,FL_ADDR[18],Location,PIN_U14,Yes +,FL_ADDR[19],Location,PIN_V14,Yes +,FL_ADDR[20],Location,PIN_U13,Yes +,FL_ADDR[21],Location,PIN_R13,Yes +,FL_DQ[0],Location,PIN_AB16,Yes +,FL_DQ[1],Location,PIN_AA16,Yes +,FL_DQ[2],Location,PIN_AB17,Yes +,FL_DQ[3],Location,PIN_AA17,Yes +,FL_DQ[4],Location,PIN_AB18,Yes +,FL_DQ[5],Location,PIN_AA18,Yes +,FL_DQ[6],Location,PIN_AB19,Yes +,FL_DQ[7],Location,PIN_AA19,Yes +,FL_OE_N,Location,PIN_AA15,Yes +,FL_RST_N,Location,PIN_W14,Yes +,FL_WE_N,Location,PIN_Y14,Yes +,FL_CE_N,Location,PIN_AB15,Yes +,SRAM_ADDR[0],Location,PIN_AA3,Yes +,SRAM_ADDR[1],Location,PIN_AB3,Yes +,SRAM_ADDR[2],Location,PIN_AA4,Yes +,SRAM_ADDR[3],Location,PIN_AB4,Yes +,SRAM_ADDR[4],Location,PIN_AA5,Yes +,SRAM_ADDR[5],Location,PIN_AB10,Yes +,SRAM_ADDR[6],Location,PIN_AA11,Yes +,SRAM_ADDR[7],Location,PIN_AB11,Yes +,SRAM_ADDR[8],Location,PIN_V11,Yes +,SRAM_ADDR[9],Location,PIN_W11,Yes +,SRAM_ADDR[10],Location,PIN_R11,Yes +,SRAM_ADDR[11],Location,PIN_T11,Yes +,SRAM_ADDR[12],Location,PIN_Y10,Yes +,SRAM_ADDR[13],Location,PIN_U10,Yes +,SRAM_ADDR[14],Location,PIN_R10,Yes +,SRAM_ADDR[15],Location,PIN_T7,Yes +,SRAM_ADDR[16],Location,PIN_Y6,Yes +,SRAM_ADDR[17],Location,PIN_Y5,Yes +,SRAM_CE_N,Location,PIN_AB5,Yes +,SRAM_DQ[0],Location,PIN_AA6,Yes +,SRAM_DQ[1],Location,PIN_AB6,Yes +,SRAM_DQ[2],Location,PIN_AA7,Yes +,SRAM_DQ[3],Location,PIN_AB7,Yes +,SRAM_DQ[4],Location,PIN_AA8,Yes +,SRAM_DQ[5],Location,PIN_AB8,Yes +,SRAM_DQ[6],Location,PIN_AA9,Yes +,SRAM_DQ[7],Location,PIN_AB9,Yes +,SRAM_DQ[8],Location,PIN_Y9,Yes +,SRAM_DQ[9],Location,PIN_W9,Yes +,SRAM_DQ[10],Location,PIN_V9,Yes +,SRAM_DQ[11],Location,PIN_U9,Yes +,SRAM_DQ[12],Location,PIN_R9,Yes +,SRAM_DQ[13],Location,PIN_W8,Yes +,SRAM_DQ[14],Location,PIN_V8,Yes +,SRAM_DQ[15],Location,PIN_U8,Yes +,SRAM_LB_N,Location,PIN_Y7,Yes +,SRAM_OE_N,Location,PIN_T8,Yes +,SRAM_UB_N,Location,PIN_W7,Yes +,SRAM_WE_N,Location,PIN_AA10,Yes +,SD_nCS,Location,PIN_U20,Yes +,SD_MOSI,Location,PIN_Y20,Yes +,SD_SCLK,Location,PIN_V20,Yes +,SD_MISO,Location,PIN_W20,Yes + diff --git a/quartus/README b/quartus/README new file mode 100644 index 0000000..47daf92 --- /dev/null +++ b/quartus/README @@ -0,0 +1,6 @@ +T65 implementation from http://www.fpgaarcade.com/resources/T65_v302.zip (bug fixes from PACE dev) +M6522 implementation from FPGA arcade VIC20 design. Various bug fixes added locally. +SN76489 implementation by Arnim Laeuger and obtained from FPGA arcade + + + diff --git a/quartus/T65/T65.vhd b/quartus/T65/T65.vhd new file mode 100644 index 0000000..4a21d79 --- /dev/null +++ b/quartus/T65/T65.vhd @@ -0,0 +1,564 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 301 more merging +-- Ver 300 Bugfixes by ehenciak added, started tidyup *bust* +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- 65xx compatible microprocessor core +-- +-- Version : 0246 +-- +-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t65/ +-- +-- Limitations : +-- +-- 65C02 and 65C816 modes are incomplete +-- Undocumented instructions are not supported +-- Some interface signals behaves incorrect +-- +-- File history : +-- +-- 0246 : First release +-- + +library IEEE; + use IEEE.std_logic_1164.all; + use IEEE.numeric_std.all; + use work.T65_Pack.all; + +-- ehenciak 2-23-2005 : Added the enable signal so that one doesn't have to use +-- the ready signal to limit the CPU. +entity T65 is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65C816 + Res_n : in std_logic; + Enable : in std_logic; + Clk : in std_logic; + Rdy : in std_logic; + Abort_n : in std_logic; + IRQ_n : in std_logic; + NMI_n : in std_logic; + SO_n : in std_logic; + R_W_n : out std_logic; + Sync : out std_logic; + EF : out std_logic; + MF : out std_logic; + XF : out std_logic; + ML_n : out std_logic; + VP_n : out std_logic; + VDA : out std_logic; + VPA : out std_logic; + A : out std_logic_vector(23 downto 0); + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0) + ); +end T65; + +architecture rtl of T65 is + + -- Registers + signal ABC, X, Y, D : std_logic_vector(15 downto 0); + signal P, AD, DL : std_logic_vector(7 downto 0) := x"00"; + signal BAH : std_logic_vector(7 downto 0); + signal BAL : std_logic_vector(8 downto 0); + signal PBR : std_logic_vector(7 downto 0); + signal DBR : std_logic_vector(7 downto 0); + signal PC : unsigned(15 downto 0); + signal S : unsigned(15 downto 0); + signal EF_i : std_logic; + signal MF_i : std_logic; + signal XF_i : std_logic; + + signal IR : std_logic_vector(7 downto 0); + signal MCycle : std_logic_vector(2 downto 0); + + signal Mode_r : std_logic_vector(1 downto 0); + signal ALU_Op_r : std_logic_vector(3 downto 0); + signal Write_Data_r : std_logic_vector(2 downto 0); + signal Set_Addr_To_r : std_logic_vector(1 downto 0); + signal PCAdder : unsigned(8 downto 0); + + signal RstCycle : std_logic; + signal IRQCycle : std_logic; + signal NMICycle : std_logic; + + signal B_o : std_logic; + signal SO_n_o : std_logic; + signal IRQ_n_o : std_logic; + signal NMI_n_o : std_logic; + signal NMIAct : std_logic; + + signal Break : std_logic; + + -- ALU signals + signal BusA : std_logic_vector(7 downto 0); + signal BusA_r : std_logic_vector(7 downto 0); + signal BusB : std_logic_vector(7 downto 0); + signal ALU_Q : std_logic_vector(7 downto 0); + signal P_Out : std_logic_vector(7 downto 0); + + -- Micro code outputs + signal LCycle : std_logic_vector(2 downto 0); + signal ALU_Op : std_logic_vector(3 downto 0); + signal Set_BusA_To : std_logic_vector(2 downto 0); + signal Set_Addr_To : std_logic_vector(1 downto 0); + signal Write_Data : std_logic_vector(2 downto 0); + signal Jump : std_logic_vector(1 downto 0); + signal BAAdd : std_logic_vector(1 downto 0); + signal BreakAtNA : std_logic; + signal ADAdd : std_logic; + signal AddY : std_logic; + signal PCAdd : std_logic; + signal Inc_S : std_logic; + signal Dec_S : std_logic; + signal LDA : std_logic; + signal LDP : std_logic; + signal LDX : std_logic; + signal LDY : std_logic; + signal LDS : std_logic; + signal LDDI : std_logic; + signal LDALU : std_logic; + signal LDAD : std_logic; + signal LDBAL : std_logic; + signal LDBAH : std_logic; + signal SaveP : std_logic; + signal Write : std_logic; + + signal really_rdy : std_logic; + signal R_W_n_i : std_logic; + +begin + -- ehenciak : gate Rdy with read/write to make an "OK, it's + -- really OK to stop the processor now if Rdy is + -- deasserted" signal + really_rdy <= Rdy or not(R_W_n_i); + + -- ehenciak : Drive R_W_n_i off chip. + R_W_n <= R_W_n_i; + + Sync <= '1' when MCycle = "000" else '0'; + EF <= EF_i; + MF <= MF_i; + XF <= XF_i; + ML_n <= '0' when IR(7 downto 6) /= "10" and IR(2 downto 1) = "11" and MCycle(2 downto 1) /= "00" else '1'; + VP_n <= '0' when IRQCycle = '1' and (MCycle = "101" or MCycle = "110") else '1'; + VDA <= '1' when Set_Addr_To_r /= "00" else '0'; -- Incorrect !!!!!!!!!!!! + VPA <= '1' when Jump(1) = '0' else '0'; -- Incorrect !!!!!!!!!!!! + + mcode : T65_MCode + port map( + Mode => Mode_r, + IR => IR, + MCycle => MCycle, + P => P, + LCycle => LCycle, + ALU_Op => ALU_Op, + Set_BusA_To => Set_BusA_To, + Set_Addr_To => Set_Addr_To, + Write_Data => Write_Data, + Jump => Jump, + BAAdd => BAAdd, + BreakAtNA => BreakAtNA, + ADAdd => ADAdd, + AddY => AddY, + PCAdd => PCAdd, + Inc_S => Inc_S, + Dec_S => Dec_S, + LDA => LDA, + LDP => LDP, + LDX => LDX, + LDY => LDY, + LDS => LDS, + LDDI => LDDI, + LDALU => LDALU, + LDAD => LDAD, + LDBAL => LDBAL, + LDBAH => LDBAH, + SaveP => SaveP, + Write => Write + ); + + alu : T65_ALU + port map( + Mode => Mode_r, + Op => ALU_Op_r, + BusA => BusA_r, + BusB => BusB, + P_In => P, + P_Out => P_Out, + Q => ALU_Q + ); + + process (Res_n, Clk) + begin + if Res_n = '0' then + PC <= (others => '0'); -- Program Counter + IR <= "00000000"; + S <= (others => '0'); -- Dummy !!!!!!!!!!!!!!!!!!!!! + D <= (others => '0'); + PBR <= (others => '0'); + DBR <= (others => '0'); + + Mode_r <= (others => '0'); + ALU_Op_r <= "1100"; + Write_Data_r <= "000"; + Set_Addr_To_r <= "00"; + + R_W_n_i <= '1'; + EF_i <= '1'; + MF_i <= '1'; + XF_i <= '1'; + + elsif Clk'event and Clk = '1' then + if (Enable = '1') then + if (really_rdy = '1') then + R_W_n_i <= not Write or RstCycle; + + D <= (others => '1'); -- Dummy + PBR <= (others => '1'); -- Dummy + DBR <= (others => '1'); -- Dummy + EF_i <= '0'; -- Dummy + MF_i <= '0'; -- Dummy + XF_i <= '0'; -- Dummy + + if MCycle = "000" then + Mode_r <= Mode; + + if IRQCycle = '0' and NMICycle = '0' then + PC <= PC + 1; + end if; + + if IRQCycle = '1' or NMICycle = '1' then + IR <= "00000000"; + else + IR <= DI; + end if; + end if; + + ALU_Op_r <= ALU_Op; + Write_Data_r <= Write_Data; + if Break = '1' then + Set_Addr_To_r <= "00"; + else + Set_Addr_To_r <= Set_Addr_To; + end if; + + if Inc_S = '1' then + S <= S + 1; + end if; + if Dec_S = '1' and RstCycle = '0' then + S <= S - 1; + end if; + if LDS = '1' then + S(7 downto 0) <= unsigned(ALU_Q); + end if; + + if IR = "00000000" and MCycle = "001" and IRQCycle = '0' and NMICycle = '0' then + PC <= PC + 1; + end if; + -- + -- jump control logic + -- + case Jump is + when "01" => + PC <= PC + 1; + + when "10" => + PC <= unsigned(DI & DL); + + when "11" => + if PCAdder(8) = '1' then + if DL(7) = '0' then + PC(15 downto 8) <= PC(15 downto 8) + 1; + else + PC(15 downto 8) <= PC(15 downto 8) - 1; + end if; + end if; + PC(7 downto 0) <= PCAdder(7 downto 0); + + when others => null; + end case; + end if; + end if; + end if; + end process; + + PCAdder <= resize(PC(7 downto 0),9) + resize(unsigned(DL(7) & DL),9) when PCAdd = '1' + else "0" & PC(7 downto 0); + + process (Clk) + begin + if Clk'event and Clk = '1' then + if (Enable = '1') then + if (really_rdy = '1') then + if MCycle = "000" then + if LDA = '1' then + ABC(7 downto 0) <= ALU_Q; + end if; + if LDX = '1' then + X(7 downto 0) <= ALU_Q; + end if; + if LDY = '1' then + Y(7 downto 0) <= ALU_Q; + end if; + if (LDA or LDX or LDY) = '1' then + P <= P_Out; + end if; + end if; + if SaveP = '1' then + P <= P_Out; + end if; + if LDP = '1' then + P <= ALU_Q; + end if; + if IR(4 downto 0) = "11000" then + case IR(7 downto 5) is + when "000" => + P(Flag_C) <= '0'; + when "001" => + P(Flag_C) <= '1'; + when "010" => + P(Flag_I) <= '0'; + when "011" => + P(Flag_I) <= '1'; + when "101" => + P(Flag_V) <= '0'; + when "110" => + P(Flag_D) <= '0'; + when "111" => + P(Flag_D) <= '1'; + when others => + end case; + end if; + + --if IR = "00000000" and MCycle = "011" and RstCycle = '0' and NMICycle = '0' and IRQCycle = '0' then + -- P(Flag_B) <= '1'; + --end if; + --if IR = "00000000" and MCycle = "100" and RstCycle = '0' and (NMICycle = '1' or IRQCycle = '1') then + -- P(Flag_I) <= '1'; + -- P(Flag_B) <= B_o; + --end if; + + -- B=1 always on the 6502 + P(Flag_B) <= '1'; + if IR = "00000000" and RstCycle = '0' and (NMICycle = '1' or IRQCycle = '1') then + if MCycle = "011" then + -- B=0 in *copy* of P pushed onto the stack + P(Flag_B) <= '0'; + elsif MCycle = "100" then + P(Flag_I) <= '1'; + end if; + end if; + + if SO_n_o = '1' and SO_n = '0' then + P(Flag_V) <= '1'; + end if; + if RstCycle = '1' and Mode_r /= "00" then + P(Flag_1) <= '1'; + P(Flag_D) <= '0'; + P(Flag_I) <= '1'; + end if; + P(Flag_1) <= '1'; + + B_o <= P(Flag_B); + SO_n_o <= SO_n; + IRQ_n_o <= IRQ_n; + NMI_n_o <= NMI_n; + end if; + end if; + end if; + end process; + +--------------------------------------------------------------------------- +-- +-- Buses +-- +--------------------------------------------------------------------------- + + process (Res_n, Clk) + begin + if Res_n = '0' then + BusA_r <= (others => '0'); + BusB <= (others => '0'); + AD <= (others => '0'); + BAL <= (others => '0'); + BAH <= (others => '0'); + DL <= (others => '0'); + elsif Clk'event and Clk = '1' then + if (Enable = '1') then + if (Rdy = '1') then + BusA_r <= BusA; + BusB <= DI; + + case BAAdd is + when "01" => + -- BA Inc + AD <= std_logic_vector(unsigned(AD) + 1); + BAL <= std_logic_vector(unsigned(BAL) + 1); + when "10" => + -- BA Add + BAL <= std_logic_vector(resize(unsigned(BAL(7 downto 0)),9) + resize(unsigned(BusA),9)); + when "11" => + -- BA Adj + if BAL(8) = '1' then + BAH <= std_logic_vector(unsigned(BAH) + 1); + end if; + when others => + end case; + + -- ehenciak : modified to use Y register as well (bugfix) + if ADAdd = '1' then + if (AddY = '1') then + AD <= std_logic_vector(unsigned(AD) + unsigned(Y(7 downto 0))); + else + AD <= std_logic_vector(unsigned(AD) + unsigned(X(7 downto 0))); + end if; + end if; + + if IR = "00000000" then + BAL <= (others => '1'); + BAH <= (others => '1'); + if RstCycle = '1' then + BAL(2 downto 0) <= "100"; + elsif NMICycle = '1' then + BAL(2 downto 0) <= "010"; + else + BAL(2 downto 0) <= "110"; + end if; + if Set_addr_To_r = "11" then + BAL(0) <= '1'; + end if; + end if; + + + if LDDI = '1' then + DL <= DI; + end if; + if LDALU = '1' then + DL <= ALU_Q; + end if; + if LDAD = '1' then + AD <= DI; + end if; + if LDBAL = '1' then + BAL(7 downto 0) <= DI; + end if; + if LDBAH = '1' then + BAH <= DI; + end if; + end if; + end if; + end if; + end process; + + Break <= (BreakAtNA and not BAL(8)) or (PCAdd and not PCAdder(8)); + + + with Set_BusA_To select + BusA <= DI when "000", + ABC(7 downto 0) when "001", + X(7 downto 0) when "010", + Y(7 downto 0) when "011", + std_logic_vector(S(7 downto 0)) when "100", + P when "101", + (others => '-') when others; + + with Set_Addr_To_r select + A <= "0000000000000001" & std_logic_vector(S(7 downto 0)) when "01", + DBR & "00000000" & AD when "10", + "00000000" & BAH & BAL(7 downto 0) when "11", + PBR & std_logic_vector(PC(15 downto 8)) & std_logic_vector(PCAdder(7 downto 0)) when others; + + with Write_Data_r select + DO <= DL when "000", + ABC(7 downto 0) when "001", + X(7 downto 0) when "010", + Y(7 downto 0) when "011", + std_logic_vector(S(7 downto 0)) when "100", + P when "101", + std_logic_vector(PC(7 downto 0)) when "110", + std_logic_vector(PC(15 downto 8)) when others; + +------------------------------------------------------------------------- +-- +-- Main state machine +-- +------------------------------------------------------------------------- + + process (Res_n, Clk) + begin + if Res_n = '0' then + MCycle <= "001"; + RstCycle <= '1'; + IRQCycle <= '0'; + NMICycle <= '0'; + NMIAct <= '0'; + elsif Clk'event and Clk = '1' then + if (Enable = '1') then + if (really_rdy = '1') then + if MCycle = LCycle or Break = '1' then + MCycle <= "000"; + RstCycle <= '0'; + IRQCycle <= '0'; + NMICycle <= '0'; + if NMIAct = '1' then + NMICycle <= '1'; + elsif IRQ_n_o = '0' and P(Flag_I) = '0' then + IRQCycle <= '1'; + end if; + else + MCycle <= std_logic_vector(unsigned(MCycle) + 1); + end if; + + if NMICycle = '1' then + NMIAct <= '0'; + end if; + if NMI_n_o = '1' and NMI_n = '0' then + NMIAct <= '1'; + end if; + end if; + end if; + end if; + end process; + +end; diff --git a/quartus/T65/T65_ALU.vhd b/quartus/T65/T65_ALU.vhd new file mode 100644 index 0000000..d9d25e1 --- /dev/null +++ b/quartus/T65/T65_ALU.vhd @@ -0,0 +1,260 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 300 Bugfixes by ehenciak added +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- 6502 compatible microprocessor core +-- +-- Version : 0245 +-- +-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t65/ +-- +-- Limitations : +-- +-- File history : +-- +-- 0245 : First version +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T65_Pack.all; + +entity T65_ALU is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816 + Op : in std_logic_vector(3 downto 0); + BusA : in std_logic_vector(7 downto 0); + BusB : in std_logic_vector(7 downto 0); + P_In : in std_logic_vector(7 downto 0); + P_Out : out std_logic_vector(7 downto 0); + Q : out std_logic_vector(7 downto 0) + ); +end T65_ALU; + +architecture rtl of T65_ALU is + + -- AddSub variables (temporary signals) + signal ADC_Z : std_logic; + signal ADC_C : std_logic; + signal ADC_V : std_logic; + signal ADC_N : std_logic; + signal ADC_Q : std_logic_vector(7 downto 0); + signal SBC_Z : std_logic; + signal SBC_C : std_logic; + signal SBC_V : std_logic; + signal SBC_N : std_logic; + signal SBC_Q : std_logic_vector(7 downto 0); + +begin + + process (P_In, BusA, BusB) + variable AL : unsigned(6 downto 0); + variable AH : unsigned(6 downto 0); + variable C : std_logic; + begin + AL := resize(unsigned(BusA(3 downto 0) & P_In(Flag_C)), 7) + resize(unsigned(BusB(3 downto 0) & "1"), 7); + AH := resize(unsigned(BusA(7 downto 4) & AL(5)), 7) + resize(unsigned(BusB(7 downto 4) & "1"), 7); + +-- pragma translate_off + if is_x(std_logic_vector(AL)) then AL := "0000000"; end if; + if is_x(std_logic_vector(AH)) then AH := "0000000"; end if; +-- pragma translate_on + + if AL(4 downto 1) = 0 and AH(4 downto 1) = 0 then + ADC_Z <= '1'; + else + ADC_Z <= '0'; + end if; + + if AL(5 downto 1) > 9 and P_In(Flag_D) = '1' then + AL(6 downto 1) := AL(6 downto 1) + 6; + end if; + + C := AL(6) or AL(5); + AH := resize(unsigned(BusA(7 downto 4) & C), 7) + resize(unsigned(BusB(7 downto 4) & "1"), 7); + + ADC_N <= AH(4); + ADC_V <= (AH(4) xor BusA(7)) and not (BusA(7) xor BusB(7)); + +-- pragma translate_off + if is_x(std_logic_vector(AH)) then AH := "0000000"; end if; +-- pragma translate_on + + if AH(5 downto 1) > 9 and P_In(Flag_D) = '1' then + AH(6 downto 1) := AH(6 downto 1) + 6; + end if; + + ADC_C <= AH(6) or AH(5); + + ADC_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1)); + end process; + + process (Op, P_In, BusA, BusB) + variable AL : unsigned(6 downto 0); + variable AH : unsigned(5 downto 0); + variable C : std_logic; + begin + C := P_In(Flag_C) or not Op(0); + AL := resize(unsigned(BusA(3 downto 0) & C), 7) - resize(unsigned(BusB(3 downto 0) & "1"), 6); + AH := resize(unsigned(BusA(7 downto 4) & "0"), 6) - resize(unsigned(BusB(7 downto 4) & AL(5)), 6); + +-- pragma translate_off + if is_x(std_logic_vector(AL)) then AL := "0000000"; end if; + if is_x(std_logic_vector(AH)) then AH := "000000"; end if; +-- pragma translate_on + + if AL(4 downto 1) = 0 and AH(4 downto 1) = 0 then + SBC_Z <= '1'; + else + SBC_Z <= '0'; + end if; + + SBC_C <= not AH(5); + SBC_V <= (AH(4) xor BusA(7)) and (BusA(7) xor BusB(7)); + SBC_N <= AH(4); + + if P_In(Flag_D) = '1' then + if AL(5) = '1' then + AL(5 downto 1) := AL(5 downto 1) - 6; + end if; + AH := resize(unsigned(BusA(7 downto 4) & "0"), 6) - resize(unsigned(BusB(7 downto 4) & AL(6)), 6); + if AH(5) = '1' then + AH(5 downto 1) := AH(5 downto 1) - 6; + end if; + end if; + + SBC_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1)); + end process; + + process (Op, P_In, BusA, BusB, + ADC_Z, ADC_C, ADC_V, ADC_N, ADC_Q, + SBC_Z, SBC_C, SBC_V, SBC_N, SBC_Q) + variable Q_t : std_logic_vector(7 downto 0); + begin + -- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC + -- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC + P_Out <= P_In; + Q_t := BusA; + case Op(3 downto 0) is + when "0000" => + -- ORA + Q_t := BusA or BusB; + when "0001" => + -- AND + Q_t := BusA and BusB; + when "0010" => + -- EOR + Q_t := BusA xor BusB; + when "0011" => + -- ADC + P_Out(Flag_V) <= ADC_V; + P_Out(Flag_C) <= ADC_C; + Q_t := ADC_Q; + when "0101" | "1101" => + -- LDA + when "0110" => + -- CMP + P_Out(Flag_C) <= SBC_C; + when "0111" => + -- SBC + P_Out(Flag_V) <= SBC_V; + P_Out(Flag_C) <= SBC_C; + Q_t := SBC_Q; + when "1000" => + -- ASL + Q_t := BusA(6 downto 0) & "0"; + P_Out(Flag_C) <= BusA(7); + when "1001" => + -- ROL + Q_t := BusA(6 downto 0) & P_In(Flag_C); + P_Out(Flag_C) <= BusA(7); + when "1010" => + -- LSR + Q_t := "0" & BusA(7 downto 1); + P_Out(Flag_C) <= BusA(0); + when "1011" => + -- ROR + Q_t := P_In(Flag_C) & BusA(7 downto 1); + P_Out(Flag_C) <= BusA(0); + when "1100" => + -- BIT + P_Out(Flag_V) <= BusB(6); + when "1110" => + -- DEC + Q_t := std_logic_vector(unsigned(BusA) - 1); + when "1111" => + -- INC + Q_t := std_logic_vector(unsigned(BusA) + 1); + when others => + end case; + + case Op(3 downto 0) is + when "0011" => + P_Out(Flag_N) <= ADC_N; + P_Out(Flag_Z) <= ADC_Z; + when "0110" | "0111" => + P_Out(Flag_N) <= SBC_N; + P_Out(Flag_Z) <= SBC_Z; + when "0100" => + when "1100" => + P_Out(Flag_N) <= BusB(7); + if (BusA and BusB) = "00000000" then + P_Out(Flag_Z) <= '1'; + else + P_Out(Flag_Z) <= '0'; + end if; + when others => + P_Out(Flag_N) <= Q_t(7); + if Q_t = "00000000" then + P_Out(Flag_Z) <= '1'; + else + P_Out(Flag_Z) <= '0'; + end if; + end case; + + Q <= Q_t; + end process; + +end; diff --git a/quartus/T65/T65_MCode.vhd b/quartus/T65/T65_MCode.vhd new file mode 100644 index 0000000..3fd40d8 --- /dev/null +++ b/quartus/T65/T65_MCode.vhd @@ -0,0 +1,1052 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 302 minor timing fixes +-- Ver 301 Jump timing fixed +-- Ver 300 Bugfixes by ehenciak added +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- 65xx compatible microprocessor core +-- +-- Version : 0246 + fix +-- +-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t65/ +-- +-- Limitations : +-- +-- 65C02 +-- supported : inc, dec, phx, plx, phy, ply +-- missing : bra, ora, lda, cmp, sbc, tsb*2, trb*2, stz*2, bit*2, wai, stp, jmp, bbr*8, bbs*8 +-- +-- File history : +-- +-- 0246 : First release +-- + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; +use work.T65_Pack.all; + +entity T65_MCode is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816 + IR : in std_logic_vector(7 downto 0); + MCycle : in std_logic_vector(2 downto 0); + P : in std_logic_vector(7 downto 0); + LCycle : out std_logic_vector(2 downto 0); + ALU_Op : out std_logic_vector(3 downto 0); + Set_BusA_To : out std_logic_vector(2 downto 0); -- DI,A,X,Y,S,P + Set_Addr_To : out std_logic_vector(1 downto 0); -- PC Adder,S,AD,BA + Write_Data : out std_logic_vector(2 downto 0); -- DL,A,X,Y,S,P,PCL,PCH + Jump : out std_logic_vector(1 downto 0); -- PC,++,DIDL,Rel + BAAdd : out std_logic_vector(1 downto 0); -- None,DB Inc,BA Add,BA Adj + BreakAtNA : out std_logic; + ADAdd : out std_logic; + AddY : out std_logic; + PCAdd : out std_logic; + Inc_S : out std_logic; + Dec_S : out std_logic; + LDA : out std_logic; + LDP : out std_logic; + LDX : out std_logic; + LDY : out std_logic; + LDS : out std_logic; + LDDI : out std_logic; + LDALU : out std_logic; + LDAD : out std_logic; + LDBAL : out std_logic; + LDBAH : out std_logic; + SaveP : out std_logic; + Write : out std_logic + ); +end T65_MCode; + +architecture rtl of T65_MCode is + + signal Branch : std_logic; + +begin + + with IR(7 downto 5) select + Branch <= not P(Flag_N) when "000", + P(Flag_N) when "001", + not P(Flag_V) when "010", + P(Flag_V) when "011", + not P(Flag_C) when "100", + P(Flag_C) when "101", + not P(Flag_Z) when "110", + P(Flag_Z) when others; + + process (IR, MCycle, P, Branch, Mode) + begin + LCycle <= "001"; + Set_BusA_To <= "001"; -- A + Set_Addr_To <= (others => '0'); + Write_Data <= (others => '0'); + Jump <= (others => '0'); + BAAdd <= "00"; + BreakAtNA <= '0'; + ADAdd <= '0'; + PCAdd <= '0'; + Inc_S <= '0'; + Dec_S <= '0'; + LDA <= '0'; + LDP <= '0'; + LDX <= '0'; + LDY <= '0'; + LDS <= '0'; + LDDI <= '0'; + LDALU <= '0'; + LDAD <= '0'; + LDBAL <= '0'; + LDBAH <= '0'; + SaveP <= '0'; + Write <= '0'; + AddY <= '0'; + + case IR(7 downto 5) is + when "100" => + --{{{ + case IR(1 downto 0) is + when "00" => + Set_BusA_To <= "011"; -- Y + Write_Data <= "011"; -- Y + when "10" => + Set_BusA_To <= "010"; -- X + Write_Data <= "010"; -- X + when others => + Write_Data <= "001"; -- A + end case; + --}}} + when "101" => + --{{{ + case IR(1 downto 0) is + when "00" => + if IR(4) /= '1' or IR(2) /= '0' then + LDY <= '1'; + end if; + when "10" => + LDX <= '1'; + when others => + LDA <= '1'; + end case; + Set_BusA_To <= "000"; -- DI + --}}} + when "110" => + --{{{ + case IR(1 downto 0) is + when "00" => + if IR(4) = '0' then + LDY <= '1'; + end if; + Set_BusA_To <= "011"; -- Y + when others => + Set_BusA_To <= "001"; -- A + end case; + --}}} + when "111" => + --{{{ + case IR(1 downto 0) is + when "00" => + if IR(4) = '0' then + LDX <= '1'; + end if; + Set_BusA_To <= "010"; -- X + when others => + Set_BusA_To <= "001"; -- A + end case; + --}}} + when others => + end case; + + if IR(7 downto 6) /= "10" and IR(1 downto 0) = "10" then + Set_BusA_To <= "000"; -- DI + end if; + + case IR(4 downto 0) is + when "00000" | "01000" | "01010" | "11000" | "11010" => + --{{{ + -- Implied + case IR is + when "00000000" => + -- BRK + LCycle <= "110"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= "01"; -- S + Write_Data <= "111"; -- PCH + Write <= '1'; + when 2 => + Dec_S <= '1'; + Set_Addr_To <= "01"; -- S + Write_Data <= "110"; -- PCL + Write <= '1'; + when 3 => + Dec_S <= '1'; + Set_Addr_To <= "01"; -- S + Write_Data <= "101"; -- P + Write <= '1'; + when 4 => + Dec_S <= '1'; + Set_Addr_To <= "11"; -- BA + when 5 => + LDDI <= '1'; + Set_Addr_To <= "11"; -- BA + when 6 => + Jump <= "10"; -- DIDL + when others => + end case; + when "00100000" => + -- JSR + LCycle <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDDI <= '1'; + Set_Addr_To <= "01"; -- S + when 2 => + Set_Addr_To <= "01"; -- S + Write_Data <= "111"; -- PCH + Write <= '1'; + when 3 => + Dec_S <= '1'; + Set_Addr_To <= "01"; -- S + Write_Data <= "110"; -- PCL + Write <= '1'; + when 4 => + Dec_S <= '1'; + when 5 => + Jump <= "10"; -- DIDL + when others => + end case; + when "01000000" => + -- RTI + LCycle <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= "01"; -- S + when 2 => + Inc_S <= '1'; + Set_Addr_To <= "01"; -- S + when 3 => + Inc_S <= '1'; + Set_Addr_To <= "01"; -- S + Set_BusA_To <= "000"; -- DI + when 4 => + LDP <= '1'; + Inc_S <= '1'; + LDDI <= '1'; + Set_Addr_To <= "01"; -- S + when 5 => + Jump <= "10"; -- DIDL + when others => + end case; + when "01100000" => + -- RTS + LCycle <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + Set_Addr_To <= "01"; -- S + when 2 => + Inc_S <= '1'; + Set_Addr_To <= "01"; -- S + when 3 => + Inc_S <= '1'; + LDDI <= '1'; + Set_Addr_To <= "01"; -- S + when 4 => + Jump <= "10"; -- DIDL + when 5 => + Jump <= "01"; + when others => + end case; + when "00001000" | "01001000" | "01011010" | "11011010" => + -- PHP, PHA, PHY*, PHX* + LCycle <= "010"; + if Mode = "00" and IR(1) = '1' then + LCycle <= "001"; + end if; + case to_integer(unsigned(MCycle)) is + when 1 => + case IR(7 downto 4) is + when "0000" => + Write_Data <= "101"; -- P + when "0100" => + Write_Data <= "001"; -- A + when "0101" => + Write_Data <= "011"; -- Y + when "1101" => + Write_Data <= "010"; -- X + when others => + end case; + Write <= '1'; + Set_Addr_To <= "01"; -- S + when 2 => + Dec_S <= '1'; + when others => + end case; + when "00101000" | "01101000" | "01111010" | "11111010" => + -- PLP, PLA, PLY*, PLX* + LCycle <= "011"; + if Mode = "00" and IR(1) = '1' then + LCycle <= "001"; + end if; + case IR(7 downto 4) is + when "0010" => + LDP <= '1'; + when "0110" => + LDA <= '1'; + when "0111" => + if Mode /= "00" then + LDY <= '1'; + end if; + when "1111" => + if Mode /= "00" then + LDX <= '1'; + end if; + when others => + end case; + case to_integer(unsigned(MCycle)) is + when 0 => + SaveP <= '1'; + when 1 => + Set_Addr_To <= "01"; -- S + when 2 => + Inc_S <= '1'; + Set_Addr_To <= "01"; -- S + when 3 => + Set_BusA_To <= "000"; -- DI + when others => + end case; + when "10100000" | "11000000" | "11100000" => + -- LDY, CPY, CPX + -- Immediate + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + when others => + end case; + when "10001000" => + -- DEY + LDY <= '1'; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Set_BusA_To <= "011"; -- Y + when others => + end case; + when "11001010" => + -- DEX + LDX <= '1'; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Set_BusA_To <= "010"; -- X + when others => + end case; + when "00011010" | "00111010" => + -- INC*, DEC* + if Mode /= "00" then + LDA <= '1'; -- A + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Set_BusA_To <= "100"; -- S + when others => + end case; + when "00001010" | "00101010" | "01001010" | "01101010" => + -- ASL, ROL, LSR, ROR + LDA <= '1'; -- A + Set_BusA_To <= "001"; -- A + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + when others => + end case; + when "10001010" | "10011000" => + -- TYA, TXA + LDA <= '1'; -- A + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + when others => + end case; + when "10101010" | "10101000" => + -- TAX, TAY + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Set_BusA_To <= "001"; -- A + when others => + end case; + when "10011010" => + -- TXS + case to_integer(unsigned(MCycle)) is + when 0 => + LDS <= '1'; + when 1 => + when others => + end case; + when "10111010" => + -- TSX + LDX <= '1'; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Set_BusA_To <= "100"; -- S + when others => + end case; + + -- when "00011000" | "00111000" | "01011000" | "01111000" | "10111000" | "11011000" | "11111000" | "11001000" | "11101000" => + -- -- CLC, SEC, CLI, SEI, CLV, CLD, SED, INY, INX + -- case to_integer(unsigned(MCycle)) is + -- when 1 => + -- when others => + -- end case; + when others => + case to_integer(unsigned(MCycle)) is + when 0 => + when others => + end case; + end case; + --}}} + + when "00001" | "00011" => + --{{{ + -- Zero Page Indexed Indirect (d,x) + LCycle <= "101"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= "10"; -- AD + when 2 => + ADAdd <= '1'; + Set_Addr_To <= "10"; -- AD + when 3 => + BAAdd <= "01"; -- DB Inc + LDBAL <= '1'; + Set_Addr_To <= "10"; -- AD + when 4 => + LDBAH <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= "11"; -- BA + when 5 => + when others => + end case; + --}}} + + when "01001" | "01011" => + --{{{ + -- Immediate + LDA <= '1'; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + when others => + end case; + + --}}} + + when "00010" | "10010" => + --{{{ + -- Immediate, KIL + LDX <= '1'; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + if IR = "10100010" then + -- LDX + Jump <= "01"; + else + -- KIL !!!!!!!!!!!!!!!!!!!!!!!!!!!!! + end if; + when others => + end case; + --}}} + + when "00100" => + --{{{ + -- Zero Page + LCycle <= "010"; + case to_integer(unsigned(MCycle)) is + when 0 => + if IR(7 downto 5) = "001" then + SaveP <= '1'; + end if; + when 1 => + Jump <= "01"; + LDAD <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= "10"; -- AD + when 2 => + when others => + end case; + --}}} + + when "00101" | "00110" | "00111" => + --{{{ + -- Zero Page + if IR(7 downto 6) /= "10" and IR(1 downto 0) = "10" then + -- Read-Modify-Write + LCycle <= "100"; + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= "10"; -- AD + when 2 => + LDDI <= '1'; + Write <= '1'; + Set_Addr_To <= "10"; -- AD + when 3 => + LDALU <= '1'; + SaveP <= '1'; + Write <= '1'; + Set_Addr_To <= "10"; -- AD + when 4 => + when others => + end case; + else + LCycle <= "010"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDAD <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= "10"; -- AD + when 2 => + when others => + end case; + end if; + --}}} + + when "01100" => + --{{{ + -- Absolute + if IR(7 downto 6) = "01" and IR(4 downto 0) = "01100" then + -- JMP + if IR(5) = '0' then + --LCycle <= "011"; + LCycle <= "010"; + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDDI <= '1'; + when 2 => + Jump <= "10"; -- DIDL + when others => + end case; + else + --LCycle <= "101"; + LCycle <= "100"; -- mikej + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDDI <= '1'; + LDBAL <= '1'; + when 2 => + LDBAH <= '1'; + if Mode /= "00" then + Jump <= "10"; -- DIDL + end if; + if Mode = "00" then + Set_Addr_To <= "11"; -- BA + end if; + when 3 => + LDDI <= '1'; + if Mode = "00" then + Set_Addr_To <= "11"; -- BA + BAAdd <= "01"; -- DB Inc + else + Jump <= "01"; + end if; + when 4 => + Jump <= "10"; -- DIDL + when others => + end case; + end if; + else + LCycle <= "011"; + case to_integer(unsigned(MCycle)) is + when 0 => + if IR(7 downto 5) = "001" then + SaveP <= '1'; + end if; + when 1 => + Jump <= "01"; + LDBAL <= '1'; + when 2 => + Jump <= "01"; + LDBAH <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= "11"; -- BA + when 3 => + when others => + end case; + end if; + --}}} + + when "01101" | "01110" | "01111" => + --{{{ + -- Absolute + if IR(7 downto 6) /= "10" and IR(1 downto 0) = "10" then + -- Read-Modify-Write + LCycle <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDBAL <= '1'; + when 2 => + Jump <= "01"; + LDBAH <= '1'; + Set_Addr_To <= "11"; -- BA + when 3 => + LDDI <= '1'; + Write <= '1'; + Set_Addr_To <= "11"; -- BA + when 4 => + Write <= '1'; + LDALU <= '1'; + SaveP <= '1'; + Set_Addr_To <= "11"; -- BA + when 5 => + SaveP <= '0'; -- MIKEJ was 1 + when others => + end case; + else + LCycle <= "011"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDBAL <= '1'; + when 2 => + Jump <= "01"; + LDBAH <= '1'; + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= "11"; -- BA + when 3 => + when others => + end case; + end if; + --}}} + + when "10000" => + --{{{ + -- Relative + + -- This circuit dictates when the last + -- microcycle occurs for the branch depending on + -- whether or not the branch is taken and if a page + -- is crossed... + if (Branch = '1') then + + LCycle <= "011"; -- We're done @ T3 if branching...upper + -- level logic will stop at T2 if no page cross + -- (See the Break signal) + else + + LCycle <= "001"; + + end if; + + -- This decodes the current microcycle and takes the + -- proper course of action... + case to_integer(unsigned(MCycle)) is + + -- On the T1 microcycle, increment the program counter + -- and instruct the upper level logic to fetch the offset + -- from the Din bus and store it in the data latches. This + -- will be the last microcycle if the branch isn't taken. + when 1 => + + Jump <= "01"; -- Increments the PC by one (PC will now be PC+2) + -- from microcycle T0. + + LDDI <= '1'; -- Tells logic in top level (T65.vhd) to route + -- the Din bus to the memory data latch (DL) + -- so that the branch offset is fetched. + + -- In microcycle T2, tell the logic in the top level to + -- add the offset. If the most significant byte of the + -- program counter (i.e. the current "page") does not need + -- updating, we are done here...the Break signal at the + -- T65.vhd level takes care of that... + when 2 => + + Jump <= "11"; -- Tell the PC Jump logic to use relative mode. + + PCAdd <= '1'; -- This tells the PC adder to update itself with + -- the current offset recently fetched from + -- memory. + + -- The following is microcycle T3 : + -- The program counter should be completely updated + -- on this cycle after the page cross is detected. + -- We don't need to do anything here... + when 3 => + + + when others => null; -- Do nothing. + + end case; + --}}} + + when "10001" | "10011" => + --{{{ + -- Zero Page Indirect Indexed (d),y + LCycle <= "101"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= "10"; -- AD + when 2 => + LDBAL <= '1'; + BAAdd <= "01"; -- DB Inc + Set_Addr_To <= "10"; -- AD + when 3 => + Set_BusA_To <= "011"; -- Y + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= "11"; -- BA + when 4 => + BAAdd <= "11"; -- BA Adj + if IR(7 downto 5) = "100" then + Write <= '1'; + else + BreakAtNA <= '1'; + end if; + Set_Addr_To <= "11"; -- BA + when 5 => + when others => + end case; + --}}} + + when "10100" | "10101" | "10110" | "10111" => + --{{{ + -- Zero Page, X + if IR(7 downto 6) /= "10" and IR(1 downto 0) = "10" then + -- Read-Modify-Write + LCycle <= "101"; + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= "10"; -- AD + when 2 => + ADAdd <= '1'; + Set_Addr_To <= "10"; -- AD + when 3 => + LDDI <= '1'; + Write <= '1'; + Set_Addr_To <= "10"; -- AD + when 4 => + LDALU <= '1'; + SaveP <= '1'; + Write <= '1'; + Set_Addr_To <= "10"; -- AD + when 5 => + when others => + end case; + else + LCycle <= "011"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDAD <= '1'; + Set_Addr_To <= "10"; -- AD + when 2 => + ADAdd <= '1'; + -- Added this check for Y reg. use... + if (IR(3 downto 0) = "0110") then + AddY <= '1'; + end if; + + if IR(7 downto 5) = "100" then + Write <= '1'; + end if; + Set_Addr_To <= "10"; -- AD + when 3 => null; + when others => + end case; + end if; + --}}} + + when "11001" | "11011" => + --{{{ + -- Absolute Y + LCycle <= "100"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDBAL <= '1'; + when 2 => + Jump <= "01"; + Set_BusA_To <= "011"; -- Y + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= "11"; -- BA + when 3 => + BAAdd <= "11"; -- BA adj + if IR(7 downto 5) = "100" then + Write <= '1'; + else + BreakAtNA <= '1'; + end if; + Set_Addr_To <= "11"; -- BA + when 4 => + when others => + end case; + --}}} + + when "11100" | "11101" | "11110" | "11111" => + --{{{ + -- Absolute X + + if IR(7 downto 6) /= "10" and IR(1 downto 0) = "10" then + -- Read-Modify-Write + LCycle <= "110"; + case to_integer(unsigned(MCycle)) is + when 1 => + Jump <= "01"; + LDBAL <= '1'; + when 2 => + Jump <= "01"; + Set_BusA_To <= "010"; -- X + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= "11"; -- BA + when 3 => + BAAdd <= "11"; -- BA adj + Set_Addr_To <= "11"; -- BA + when 4 => + LDDI <= '1'; + Write <= '1'; + Set_Addr_To <= "11"; -- BA + when 5 => + LDALU <= '1'; + SaveP <= '1'; + Write <= '1'; + Set_Addr_To <= "11"; -- BA + when 6 => + when others => + end case; + else + LCycle <= "100"; + if IR(7 downto 6) /= "10" then + LDA <= '1'; + end if; + case to_integer(unsigned(MCycle)) is + when 0 => + when 1 => + Jump <= "01"; + LDBAL <= '1'; + when 2 => + Jump <= "01"; + -- mikej + -- special case 0xBE which uses Y reg as index!! + if (IR = "10111110") then + Set_BusA_To <= "011"; -- Y + else + Set_BusA_To <= "010"; -- X + end if; + BAAdd <= "10"; -- BA Add + LDBAH <= '1'; + Set_Addr_To <= "11"; -- BA + when 3 => + BAAdd <= "11"; -- BA adj + if IR(7 downto 5) = "100" then + Write <= '1'; + else + BreakAtNA <= '1'; + end if; + Set_Addr_To <= "11"; -- BA + when 4 => + when others => + end case; + end if; + --}}} + when others => + end case; + end process; + + process (IR, MCycle) + begin + -- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC + -- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC + case IR(1 downto 0) is + when "00" => + --{{{ + case IR(4 downto 2) is + when "000" | "001" | "011" => + case IR(7 downto 5) is + when "110" | "111" => + -- CP + ALU_Op <= "0110"; + when "101" => + -- LD + ALU_Op <= "0101"; + when "001" => + -- BIT + ALU_Op <= "1100"; + when others => + -- NOP/ST + ALU_Op <= "0100"; + end case; + when "010" => + case IR(7 downto 5) is + when "111" | "110" => + -- IN + ALU_Op <= "1111"; + when "100" => + -- DEY + ALU_Op <= "1110"; + when others => + -- LD + ALU_Op <= "1101"; + end case; + when "110" => + case IR(7 downto 5) is + when "100" => + -- TYA + ALU_Op <= "1101"; + when others => + ALU_Op <= "----"; + end case; + when others => + case IR(7 downto 5) is + when "101" => + -- LD + ALU_Op <= "1101"; + when others => + ALU_Op <= "0100"; + end case; + end case; + --}}} + when "01" => -- OR + --{{{ + ALU_Op(3) <= '0'; + ALU_Op(2 downto 0) <= IR(7 downto 5); + --}}} + when "10" => + --{{{ + ALU_Op(3) <= '1'; + ALU_Op(2 downto 0) <= IR(7 downto 5); + case IR(7 downto 5) is + when "000" => + if IR(4 downto 2) = "110" then + -- INC + ALU_Op <= "1111"; + end if; + when "001" => + if IR(4 downto 2) = "110" then + -- DEC + ALU_Op <= "1110"; + end if; + when "100" => + if IR(4 downto 2) = "010" then + -- TXA + ALU_Op <= "0101"; + else + ALU_Op <= "0100"; + end if; + when others => + end case; + --}}} + when others => + --{{{ + case IR(7 downto 5) is + when "100" => + ALU_Op <= "0100"; + when others => + if MCycle = "000" then + ALU_Op(3) <= '0'; + ALU_Op(2 downto 0) <= IR(7 downto 5); + else + ALU_Op(3) <= '1'; + ALU_Op(2 downto 0) <= IR(7 downto 5); + end if; + end case; + --}}} + end case; + end process; + +end; diff --git a/quartus/T65/T65_Pack.vhd b/quartus/T65/T65_Pack.vhd new file mode 100644 index 0000000..f8d603c --- /dev/null +++ b/quartus/T65/T65_Pack.vhd @@ -0,0 +1,117 @@ +-- **** +-- T65(b) core. In an effort to merge and maintain bug fixes .... +-- +-- +-- Ver 300 Bugfixes by ehenciak added +-- MikeJ March 2005 +-- Latest version from www.fpgaarcade.com (original www.opencores.org) +-- +-- **** +-- +-- 65xx compatible microprocessor core +-- +-- Version : 0246 +-- +-- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +-- The latest version of this file can be found at: +-- http://www.opencores.org/cvsweb.shtml/t65/ +-- +-- Limitations : +-- +-- File history : +-- + +library IEEE; +use IEEE.std_logic_1164.all; + +package T65_Pack is + + constant Flag_C : integer := 0; + constant Flag_Z : integer := 1; + constant Flag_I : integer := 2; + constant Flag_D : integer := 3; + constant Flag_B : integer := 4; + constant Flag_1 : integer := 5; + constant Flag_V : integer := 6; + constant Flag_N : integer := 7; + + component T65_MCode + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816 + IR : in std_logic_vector(7 downto 0); + MCycle : in std_logic_vector(2 downto 0); + P : in std_logic_vector(7 downto 0); + LCycle : out std_logic_vector(2 downto 0); + ALU_Op : out std_logic_vector(3 downto 0); + Set_BusA_To : out std_logic_vector(2 downto 0); -- DI,A,X,Y,S,P + Set_Addr_To : out std_logic_vector(1 downto 0); -- PC Adder,S,AD,BA + Write_Data : out std_logic_vector(2 downto 0); -- DL,A,X,Y,S,P,PCL,PCH + Jump : out std_logic_vector(1 downto 0); -- PC,++,DIDL,Rel + BAAdd : out std_logic_vector(1 downto 0); -- None,DB Inc,BA Add,BA Adj + BreakAtNA : out std_logic; + ADAdd : out std_logic; + AddY : out std_logic; + PCAdd : out std_logic; + Inc_S : out std_logic; + Dec_S : out std_logic; + LDA : out std_logic; + LDP : out std_logic; + LDX : out std_logic; + LDY : out std_logic; + LDS : out std_logic; + LDDI : out std_logic; + LDALU : out std_logic; + LDAD : out std_logic; + LDBAL : out std_logic; + LDBAH : out std_logic; + SaveP : out std_logic; + Write : out std_logic + ); + end component; + + component T65_ALU + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65C816 + Op : in std_logic_vector(3 downto 0); + BusA : in std_logic_vector(7 downto 0); + BusB : in std_logic_vector(7 downto 0); + P_In : in std_logic_vector(7 downto 0); + P_Out : out std_logic_vector(7 downto 0); + Q : out std_logic_vector(7 downto 0) + ); + end component; + +end; diff --git a/quartus/bbc_micro.qpf b/quartus/bbc_micro.qpf new file mode 100644 index 0000000..52e0fd0 --- /dev/null +++ b/quartus/bbc_micro.qpf @@ -0,0 +1,30 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 1991-2009 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus II +# Version 9.1 Build 222 10/21/2009 SJ Web Edition +# Date created = 20:48:44 July 12, 2011 +# +# -------------------------------------------------------------------------- # + +QUARTUS_VERSION = "9.1" +DATE = "20:48:44 July 12, 2011" + +# Revisions + +PROJECT_REVISION = "bbc_micro_de1" diff --git a/quartus/bbc_micro_de1.qsf b/quartus/bbc_micro_de1.qsf new file mode 100644 index 0000000..be8b1f2 --- /dev/null +++ b/quartus/bbc_micro_de1.qsf @@ -0,0 +1,537 @@ +# -------------------------------------------------------------------------- # +# +# Copyright (C) 1991-2009 Altera Corporation +# Your use of Altera Corporation's design tools, logic functions +# and other software and tools, and its AMPP partner logic +# functions, and any output files from any of the foregoing +# (including device programming or simulation files), and any +# associated documentation or information are expressly subject +# to the terms and conditions of the Altera Program License +# Subscription Agreement, Altera MegaCore Function License +# Agreement, or other applicable license agreement, including, +# without limitation, that your use is for the sole purpose of +# programming logic devices manufactured by Altera and sold by +# Altera or its authorized distributors. Please refer to the +# applicable agreement for further details. +# +# -------------------------------------------------------------------------- # +# +# Quartus II +# Version 9.1 Build 222 10/21/2009 SJ Web Edition +# Date created = 20:48:44 July 12, 2011 +# +# -------------------------------------------------------------------------- # +# +# Notes: +# +# 1) The default values for assignments are stored in the file: +# bbc_micro_de1_assignment_defaults.qdf +# If this file doesn't exist, see file: +# assignment_defaults.qdf +# +# 2) Altera recommends that you do not modify this file. This +# file is updated automatically by the Quartus II software +# and any changes you make may be lost or overwritten. +# +# -------------------------------------------------------------------------- # + + +set_global_assignment -name FAMILY "Cyclone II" +set_global_assignment -name DEVICE EP2C20F484C7 +set_global_assignment -name TOP_LEVEL_ENTITY bbc_micro_de1 +set_global_assignment -name ORIGINAL_QUARTUS_VERSION 9.1 +set_global_assignment -name PROJECT_CREATION_TIME_DATE "20:48:44 JULY 12, 2011" +set_global_assignment -name LAST_QUARTUS_VERSION 9.1 +set_global_assignment -name USE_GENERATED_PHYSICAL_CONSTRAINTS OFF -section_id eda_blast_fpga +set_global_assignment -name DEVICE_FILTER_PACKAGE FBGA +set_global_assignment -name DEVICE_FILTER_PIN_COUNT 484 +set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 7 +set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 +set_global_assignment -name MAX_CORE_JUNCTION_TEMP 85 +set_location_assignment PIN_A13 -to GPIO_0[0] +set_location_assignment PIN_B13 -to GPIO_0[1] +set_location_assignment PIN_A14 -to GPIO_0[2] +set_location_assignment PIN_B14 -to GPIO_0[3] +set_location_assignment PIN_A15 -to GPIO_0[4] +set_location_assignment PIN_B15 -to GPIO_0[5] +set_location_assignment PIN_A16 -to GPIO_0[6] +set_location_assignment PIN_B16 -to GPIO_0[7] +set_location_assignment PIN_A17 -to GPIO_0[8] +set_location_assignment PIN_B17 -to GPIO_0[9] +set_location_assignment PIN_A18 -to GPIO_0[10] +set_location_assignment PIN_B18 -to GPIO_0[11] +set_location_assignment PIN_A19 -to GPIO_0[12] +set_location_assignment PIN_B19 -to GPIO_0[13] +set_location_assignment PIN_A20 -to GPIO_0[14] +set_location_assignment PIN_B20 -to GPIO_0[15] +set_location_assignment PIN_C21 -to GPIO_0[16] +set_location_assignment PIN_C22 -to GPIO_0[17] +set_location_assignment PIN_D21 -to GPIO_0[18] +set_location_assignment PIN_D22 -to GPIO_0[19] +set_location_assignment PIN_E21 -to GPIO_0[20] +set_location_assignment PIN_E22 -to GPIO_0[21] +set_location_assignment PIN_F21 -to GPIO_0[22] +set_location_assignment PIN_F22 -to GPIO_0[23] +set_location_assignment PIN_G21 -to GPIO_0[24] +set_location_assignment PIN_G22 -to GPIO_0[25] +set_location_assignment PIN_J21 -to GPIO_0[26] +set_location_assignment PIN_J22 -to GPIO_0[27] +set_location_assignment PIN_K21 -to GPIO_0[28] +set_location_assignment PIN_K22 -to GPIO_0[29] +set_location_assignment PIN_J19 -to GPIO_0[30] +set_location_assignment PIN_J20 -to GPIO_0[31] +set_location_assignment PIN_J18 -to GPIO_0[32] +set_location_assignment PIN_K20 -to GPIO_0[33] +set_location_assignment PIN_L19 -to GPIO_0[34] +set_location_assignment PIN_L18 -to GPIO_0[35] +set_location_assignment PIN_H12 -to GPIO_1[0] +set_location_assignment PIN_H13 -to GPIO_1[1] +set_location_assignment PIN_H14 -to GPIO_1[2] +set_location_assignment PIN_G15 -to GPIO_1[3] +set_location_assignment PIN_E14 -to GPIO_1[4] +set_location_assignment PIN_E15 -to GPIO_1[5] +set_location_assignment PIN_F15 -to GPIO_1[6] +set_location_assignment PIN_G16 -to GPIO_1[7] +set_location_assignment PIN_F12 -to GPIO_1[8] +set_location_assignment PIN_F13 -to GPIO_1[9] +set_location_assignment PIN_C14 -to GPIO_1[10] +set_location_assignment PIN_D14 -to GPIO_1[11] +set_location_assignment PIN_D15 -to GPIO_1[12] +set_location_assignment PIN_D16 -to GPIO_1[13] +set_location_assignment PIN_C17 -to GPIO_1[14] +set_location_assignment PIN_C18 -to GPIO_1[15] +set_location_assignment PIN_C19 -to GPIO_1[16] +set_location_assignment PIN_C20 -to GPIO_1[17] +set_location_assignment PIN_D19 -to GPIO_1[18] +set_location_assignment PIN_D20 -to GPIO_1[19] +set_location_assignment PIN_E20 -to GPIO_1[20] +set_location_assignment PIN_F20 -to GPIO_1[21] +set_location_assignment PIN_E19 -to GPIO_1[22] +set_location_assignment PIN_E18 -to GPIO_1[23] +set_location_assignment PIN_G20 -to GPIO_1[24] +set_location_assignment PIN_G18 -to GPIO_1[25] +set_location_assignment PIN_G17 -to GPIO_1[26] +set_location_assignment PIN_H17 -to GPIO_1[27] +set_location_assignment PIN_J15 -to GPIO_1[28] +set_location_assignment PIN_H18 -to GPIO_1[29] +set_location_assignment PIN_N22 -to GPIO_1[30] +set_location_assignment PIN_N21 -to GPIO_1[31] +set_location_assignment PIN_P15 -to GPIO_1[32] +set_location_assignment PIN_N15 -to GPIO_1[33] +set_location_assignment PIN_P17 -to GPIO_1[34] +set_location_assignment PIN_P18 -to GPIO_1[35] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[0] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[1] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[2] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[3] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[4] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[5] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[6] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[7] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[8] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[9] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[10] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[11] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[12] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[13] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[14] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[15] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[16] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[17] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[18] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[19] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[20] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[21] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[22] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[23] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[24] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[25] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[26] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[27] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[28] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[29] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[30] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[31] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[32] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[33] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[34] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_0[35] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[0] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[1] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[2] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[3] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[4] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[5] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[6] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[7] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[8] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[9] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[10] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[11] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[12] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[13] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[14] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[15] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[16] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[17] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[18] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[19] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[20] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[21] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[22] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[23] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[24] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[25] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[26] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[27] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[28] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[29] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[30] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[31] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[32] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[33] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[34] +set_instance_assignment -name IO_STANDARD LVTTL -to GPIO_1[35] +set_location_assignment PIN_L22 -to SW[0] +set_location_assignment PIN_L21 -to SW[1] +set_location_assignment PIN_M22 -to SW[2] +set_location_assignment PIN_V12 -to SW[3] +set_location_assignment PIN_W12 -to SW[4] +set_location_assignment PIN_U12 -to SW[5] +set_location_assignment PIN_U11 -to SW[6] +set_location_assignment PIN_M2 -to SW[7] +set_location_assignment PIN_M1 -to SW[8] +set_location_assignment PIN_L2 -to SW[9] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[0] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[1] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[2] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[3] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[4] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[5] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[6] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[7] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[8] +set_instance_assignment -name IO_STANDARD LVTTL -to SW[9] +set_location_assignment PIN_J2 -to HEX0[0] +set_location_assignment PIN_J1 -to HEX0[1] +set_location_assignment PIN_H2 -to HEX0[2] +set_location_assignment PIN_H1 -to HEX0[3] +set_location_assignment PIN_F2 -to HEX0[4] +set_location_assignment PIN_F1 -to HEX0[5] +set_location_assignment PIN_E2 -to HEX0[6] +set_location_assignment PIN_E1 -to HEX1[0] +set_location_assignment PIN_H6 -to HEX1[1] +set_location_assignment PIN_H5 -to HEX1[2] +set_location_assignment PIN_H4 -to HEX1[3] +set_location_assignment PIN_G3 -to HEX1[4] +set_location_assignment PIN_D2 -to HEX1[5] +set_location_assignment PIN_D1 -to HEX1[6] +set_location_assignment PIN_G5 -to HEX2[0] +set_location_assignment PIN_G6 -to HEX2[1] +set_location_assignment PIN_C2 -to HEX2[2] +set_location_assignment PIN_C1 -to HEX2[3] +set_location_assignment PIN_E3 -to HEX2[4] +set_location_assignment PIN_E4 -to HEX2[5] +set_location_assignment PIN_D3 -to HEX2[6] +set_location_assignment PIN_F4 -to HEX3[0] +set_location_assignment PIN_D5 -to HEX3[1] +set_location_assignment PIN_D6 -to HEX3[2] +set_location_assignment PIN_J4 -to HEX3[3] +set_location_assignment PIN_L8 -to HEX3[4] +set_location_assignment PIN_F3 -to HEX3[5] +set_location_assignment PIN_D4 -to HEX3[6] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[0] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[1] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[2] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[3] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[4] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[5] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX0[6] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[0] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[1] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[2] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[3] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[4] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[5] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX1[6] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[0] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[1] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[2] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[3] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[4] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[5] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX2[6] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[0] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[1] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[2] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[3] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[4] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[5] +set_instance_assignment -name IO_STANDARD LVTTL -to HEX3[6] +set_location_assignment PIN_R22 -to KEY[0] +set_location_assignment PIN_R21 -to KEY[1] +set_location_assignment PIN_T22 -to KEY[2] +set_location_assignment PIN_T21 -to KEY[3] +set_location_assignment PIN_R20 -to LEDR[0] +set_location_assignment PIN_R19 -to LEDR[1] +set_location_assignment PIN_U19 -to LEDR[2] +set_location_assignment PIN_Y19 -to LEDR[3] +set_location_assignment PIN_T18 -to LEDR[4] +set_location_assignment PIN_V19 -to LEDR[5] +set_location_assignment PIN_Y18 -to LEDR[6] +set_location_assignment PIN_U18 -to LEDR[7] +set_location_assignment PIN_R18 -to LEDR[8] +set_location_assignment PIN_R17 -to LEDR[9] +set_location_assignment PIN_U22 -to LEDG[0] +set_location_assignment PIN_U21 -to LEDG[1] +set_location_assignment PIN_V22 -to LEDG[2] +set_location_assignment PIN_V21 -to LEDG[3] +set_location_assignment PIN_W22 -to LEDG[4] +set_location_assignment PIN_W21 -to LEDG[5] +set_location_assignment PIN_Y22 -to LEDG[6] +set_location_assignment PIN_Y21 -to LEDG[7] +set_instance_assignment -name IO_STANDARD LVTTL -to KEY[0] +set_instance_assignment -name IO_STANDARD LVTTL -to KEY[1] +set_instance_assignment -name IO_STANDARD LVTTL -to KEY[2] +set_instance_assignment -name IO_STANDARD LVTTL -to KEY[3] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[0] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[1] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[2] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[3] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[4] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[5] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[6] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[7] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[8] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDR[9] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[0] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[1] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[2] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[3] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[4] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[5] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[6] +set_instance_assignment -name IO_STANDARD LVTTL -to LEDG[7] +set_location_assignment PIN_D12 -to CLOCK_27[0] +set_location_assignment PIN_E12 -to CLOCK_27[1] +set_location_assignment PIN_B12 -to CLOCK_24[0] +set_location_assignment PIN_A12 -to CLOCK_24[1] +set_location_assignment PIN_L1 -to CLOCK_50 +set_location_assignment PIN_M21 -to EXT_CLOCK +set_instance_assignment -name IO_STANDARD LVTTL -to CLOCK_27[1] +set_instance_assignment -name IO_STANDARD LVTTL -to CLOCK_24[0] +set_instance_assignment -name IO_STANDARD LVTTL -to CLOCK_24[1] +set_instance_assignment -name IO_STANDARD LVTTL -to CLOCK_50 +set_instance_assignment -name IO_STANDARD LVTTL -to EXT_CLOCK +set_location_assignment PIN_H15 -to PS2_CLK +set_location_assignment PIN_J14 -to PS2_DAT +set_location_assignment PIN_F14 -to UART_RXD +set_location_assignment PIN_G12 -to UART_TXD +set_instance_assignment -name IO_STANDARD LVTTL -to PS2_CLK +set_instance_assignment -name IO_STANDARD LVTTL -to PS2_DAT +set_instance_assignment -name IO_STANDARD LVTTL -to UART_RXD +set_instance_assignment -name IO_STANDARD LVTTL -to UART_TXD +set_location_assignment PIN_E8 -to TDI +set_location_assignment PIN_D8 -to TCS +set_location_assignment PIN_C7 -to TCK +set_location_assignment PIN_D7 -to TDO +set_instance_assignment -name IO_STANDARD LVTTL -to TDI +set_instance_assignment -name IO_STANDARD LVTTL -to TCS +set_instance_assignment -name IO_STANDARD LVTTL -to TCK +set_instance_assignment -name IO_STANDARD LVTTL -to TDO +set_location_assignment PIN_D9 -to VGA_R[0] +set_location_assignment PIN_C9 -to VGA_R[1] +set_location_assignment PIN_A7 -to VGA_R[2] +set_location_assignment PIN_B7 -to VGA_R[3] +set_location_assignment PIN_B8 -to VGA_G[0] +set_location_assignment PIN_C10 -to VGA_G[1] +set_location_assignment PIN_B9 -to VGA_G[2] +set_location_assignment PIN_A8 -to VGA_G[3] +set_location_assignment PIN_A9 -to VGA_B[0] +set_location_assignment PIN_D11 -to VGA_B[1] +set_location_assignment PIN_A10 -to VGA_B[2] +set_location_assignment PIN_B10 -to VGA_B[3] +set_location_assignment PIN_A11 -to VGA_HS +set_location_assignment PIN_B11 -to VGA_VS +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_R[0] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_R[1] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_R[2] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_R[3] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_G[0] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_G[1] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_G[2] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_G[3] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_B[0] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_B[1] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_B[2] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_B[3] +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_HS +set_instance_assignment -name IO_STANDARD LVTTL -to VGA_VS +set_location_assignment PIN_A3 -to I2C_SCLK +set_location_assignment PIN_B3 -to I2C_SDAT +set_location_assignment PIN_A6 -to AUD_ADCLRCK +set_location_assignment PIN_B6 -to AUD_ADCDAT +set_location_assignment PIN_A5 -to AUD_DACLRCK +set_location_assignment PIN_B5 -to AUD_DACDAT +set_location_assignment PIN_B4 -to AUD_XCK +set_location_assignment PIN_A4 -to AUD_BCLK +set_instance_assignment -name IO_STANDARD LVTTL -to I2C_SCLK +set_instance_assignment -name IO_STANDARD LVTTL -to I2C_SDAT +set_instance_assignment -name IO_STANDARD LVTTL -to AUD_ADCLRCK +set_instance_assignment -name IO_STANDARD LVTTL -to AUD_ADCDAT +set_instance_assignment -name IO_STANDARD LVTTL -to AUD_DACLRCK +set_instance_assignment -name IO_STANDARD LVTTL -to AUD_DACDAT +set_instance_assignment -name IO_STANDARD LVTTL -to AUD_XCK +set_instance_assignment -name IO_STANDARD LVTTL -to AUD_BCLK +set_location_assignment PIN_W4 -to DRAM_ADDR[0] +set_location_assignment PIN_W5 -to DRAM_ADDR[1] +set_location_assignment PIN_Y3 -to DRAM_ADDR[2] +set_location_assignment PIN_Y4 -to DRAM_ADDR[3] +set_location_assignment PIN_R6 -to DRAM_ADDR[4] +set_location_assignment PIN_R5 -to DRAM_ADDR[5] +set_location_assignment PIN_P6 -to DRAM_ADDR[6] +set_location_assignment PIN_P5 -to DRAM_ADDR[7] +set_location_assignment PIN_P3 -to DRAM_ADDR[8] +set_location_assignment PIN_N4 -to DRAM_ADDR[9] +set_location_assignment PIN_W3 -to DRAM_ADDR[10] +set_location_assignment PIN_N6 -to DRAM_ADDR[11] +set_location_assignment PIN_U3 -to DRAM_BA_0 +set_location_assignment PIN_V4 -to DRAM_BA_1 +set_location_assignment PIN_T3 -to DRAM_CAS_N +set_location_assignment PIN_N3 -to DRAM_CKE +set_location_assignment PIN_U4 -to DRAM_CLK +set_location_assignment PIN_T6 -to DRAM_CS_N +set_location_assignment PIN_U1 -to DRAM_DQ[0] +set_location_assignment PIN_U2 -to DRAM_DQ[1] +set_location_assignment PIN_V1 -to DRAM_DQ[2] +set_location_assignment PIN_V2 -to DRAM_DQ[3] +set_location_assignment PIN_W1 -to DRAM_DQ[4] +set_location_assignment PIN_W2 -to DRAM_DQ[5] +set_location_assignment PIN_Y1 -to DRAM_DQ[6] +set_location_assignment PIN_Y2 -to DRAM_DQ[7] +set_location_assignment PIN_N1 -to DRAM_DQ[8] +set_location_assignment PIN_N2 -to DRAM_DQ[9] +set_location_assignment PIN_P1 -to DRAM_DQ[10] +set_location_assignment PIN_P2 -to DRAM_DQ[11] +set_location_assignment PIN_R1 -to DRAM_DQ[12] +set_location_assignment PIN_R2 -to DRAM_DQ[13] +set_location_assignment PIN_T1 -to DRAM_DQ[14] +set_location_assignment PIN_T2 -to DRAM_DQ[15] +set_location_assignment PIN_R7 -to DRAM_LDQM +set_location_assignment PIN_T5 -to DRAM_RAS_N +set_location_assignment PIN_M5 -to DRAM_UDQM +set_location_assignment PIN_R8 -to DRAM_WE_N +set_location_assignment PIN_AB20 -to FL_ADDR[0] +set_location_assignment PIN_AA14 -to FL_ADDR[1] +set_location_assignment PIN_Y16 -to FL_ADDR[2] +set_location_assignment PIN_R15 -to FL_ADDR[3] +set_location_assignment PIN_T15 -to FL_ADDR[4] +set_location_assignment PIN_U15 -to FL_ADDR[5] +set_location_assignment PIN_V15 -to FL_ADDR[6] +set_location_assignment PIN_W15 -to FL_ADDR[7] +set_location_assignment PIN_R14 -to FL_ADDR[8] +set_location_assignment PIN_Y13 -to FL_ADDR[9] +set_location_assignment PIN_R12 -to FL_ADDR[10] +set_location_assignment PIN_T12 -to FL_ADDR[11] +set_location_assignment PIN_AB14 -to FL_ADDR[12] +set_location_assignment PIN_AA13 -to FL_ADDR[13] +set_location_assignment PIN_AB13 -to FL_ADDR[14] +set_location_assignment PIN_AA12 -to FL_ADDR[15] +set_location_assignment PIN_AB12 -to FL_ADDR[16] +set_location_assignment PIN_AA20 -to FL_ADDR[17] +set_location_assignment PIN_U14 -to FL_ADDR[18] +set_location_assignment PIN_V14 -to FL_ADDR[19] +set_location_assignment PIN_U13 -to FL_ADDR[20] +set_location_assignment PIN_R13 -to FL_ADDR[21] +set_location_assignment PIN_AB16 -to FL_DQ[0] +set_location_assignment PIN_AA16 -to FL_DQ[1] +set_location_assignment PIN_AB17 -to FL_DQ[2] +set_location_assignment PIN_AA17 -to FL_DQ[3] +set_location_assignment PIN_AB18 -to FL_DQ[4] +set_location_assignment PIN_AA18 -to FL_DQ[5] +set_location_assignment PIN_AB19 -to FL_DQ[6] +set_location_assignment PIN_AA19 -to FL_DQ[7] +set_location_assignment PIN_AA15 -to FL_OE_N +set_location_assignment PIN_W14 -to FL_RST_N +set_location_assignment PIN_Y14 -to FL_WE_N +set_location_assignment PIN_AA3 -to SRAM_ADDR[0] +set_location_assignment PIN_AB3 -to SRAM_ADDR[1] +set_location_assignment PIN_AA4 -to SRAM_ADDR[2] +set_location_assignment PIN_AB4 -to SRAM_ADDR[3] +set_location_assignment PIN_AA5 -to SRAM_ADDR[4] +set_location_assignment PIN_AB10 -to SRAM_ADDR[5] +set_location_assignment PIN_AA11 -to SRAM_ADDR[6] +set_location_assignment PIN_AB11 -to SRAM_ADDR[7] +set_location_assignment PIN_V11 -to SRAM_ADDR[8] +set_location_assignment PIN_W11 -to SRAM_ADDR[9] +set_location_assignment PIN_R11 -to SRAM_ADDR[10] +set_location_assignment PIN_T11 -to SRAM_ADDR[11] +set_location_assignment PIN_Y10 -to SRAM_ADDR[12] +set_location_assignment PIN_U10 -to SRAM_ADDR[13] +set_location_assignment PIN_R10 -to SRAM_ADDR[14] +set_location_assignment PIN_T7 -to SRAM_ADDR[15] +set_location_assignment PIN_Y6 -to SRAM_ADDR[16] +set_location_assignment PIN_Y5 -to SRAM_ADDR[17] +set_location_assignment PIN_AB5 -to SRAM_CE_N +set_location_assignment PIN_AA6 -to SRAM_DQ[0] +set_location_assignment PIN_AB6 -to SRAM_DQ[1] +set_location_assignment PIN_AA7 -to SRAM_DQ[2] +set_location_assignment PIN_AB7 -to SRAM_DQ[3] +set_location_assignment PIN_AA8 -to SRAM_DQ[4] +set_location_assignment PIN_AB8 -to SRAM_DQ[5] +set_location_assignment PIN_AA9 -to SRAM_DQ[6] +set_location_assignment PIN_AB9 -to SRAM_DQ[7] +set_location_assignment PIN_Y9 -to SRAM_DQ[8] +set_location_assignment PIN_W9 -to SRAM_DQ[9] +set_location_assignment PIN_V9 -to SRAM_DQ[10] +set_location_assignment PIN_U9 -to SRAM_DQ[11] +set_location_assignment PIN_R9 -to SRAM_DQ[12] +set_location_assignment PIN_W8 -to SRAM_DQ[13] +set_location_assignment PIN_V8 -to SRAM_DQ[14] +set_location_assignment PIN_U8 -to SRAM_DQ[15] +set_location_assignment PIN_Y7 -to SRAM_LB_N +set_location_assignment PIN_T8 -to SRAM_OE_N +set_location_assignment PIN_W7 -to SRAM_UB_N +set_location_assignment PIN_AA10 -to SRAM_WE_N +set_global_assignment -name RESERVE_ALL_UNUSED_PINS "AS INPUT TRI-STATED WITH WEAK PULL-UP" +set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top +set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top +set_global_assignment -name LL_ROOT_REGION ON -section_id "Root Region" +set_global_assignment -name LL_MEMBER_STATE LOCKED -section_id "Root Region" +set_global_assignment -name MISC_FILE "U:/git_repos/fpga/bbc/bbc_micro_de1.dpf" +set_location_assignment PIN_AB15 -to FL_CE_N +set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL" +set_global_assignment -name STRATIX_CONFIGURATION_DEVICE EPCS4 +set_global_assignment -name RESERVE_ASDO_AFTER_CONFIGURATION "AS INPUT TRI-STATED" +set_global_assignment -name CYCLONEII_RESERVE_NCEO_AFTER_CONFIGURATION "USE AS REGULAR IO" +set_location_assignment PIN_U20 -to SD_nCS +set_location_assignment PIN_V20 -to SD_SCLK +set_location_assignment PIN_Y20 -to SD_MOSI +set_location_assignment PIN_W20 -to SD_MISO +set_global_assignment -name VHDL_FILE saa5050.vhd +set_global_assignment -name VHDL_FILE i2s_intf.vhd +set_global_assignment -name VHDL_FILE i2c_loader.vhd +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_comp_pack-p.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_noise.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_tone.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_top.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_attenuator.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_clock_div.vhd" +set_global_assignment -name VHDL_FILE "sn76489-1.0/sn76489_latch_ctrl.vhd" +set_global_assignment -name VHDL_FILE ps2_intf.vhd +set_global_assignment -name VHDL_FILE m6522.vhd +set_global_assignment -name VHDL_FILE seg7.vhd +set_global_assignment -name VHDL_FILE vidproc.vhd +set_global_assignment -name VHDL_FILE mc6845.vhd +set_global_assignment -name VHDL_FILE T65/T65_Pack.vhd +set_global_assignment -name VHDL_FILE T65/T65.vhd +set_global_assignment -name VHDL_FILE T65/T65_ALU.vhd +set_global_assignment -name VHDL_FILE T65/T65_MCode.vhd +set_global_assignment -name QIP_FILE pll32.qip +set_global_assignment -name VHDL_FILE bbc_micro_de1.vhd +set_global_assignment -name VHDL_FILE bbc_micro_de1_tb.vhd +set_global_assignment -name VHDL_FILE m6522_tb.vhd +set_global_assignment -name VHDL_FILE keyboard.vhd +set_global_assignment -name VHDL_FILE debugger.vhd +set_global_assignment -name QIP_FILE saa5050_rom.qip +set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/quartus/bbc_micro_de1.vhd b/quartus/bbc_micro_de1.vhd new file mode 100644 index 0000000..e109c36 --- /dev/null +++ b/quartus/bbc_micro_de1.vhd @@ -0,0 +1,1265 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- BBC B Micro +-- +-- Terasic DE1 top-level +-- +-- (C) 2011 Mike Stirling + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +-- Generic top-level entity for Altera DE1 board +entity bbc_micro_de1 is +generic ( + -- ROM offset + -- The 4MB Flash is used in 16KB banks as a simple mechanism for + -- different machines to address different parts of the ROM, saving + -- on re-flashing each time a new machine is run on the board. + -- This generic sets the upper 8 address bits. + -- Note that the lower bits may be ignored by the implementation, + -- e.g. where ROMs are bigger than 16K or where multiple banks + -- are required. In this case it is important to ensure that the + -- ROM images are aligned correctly (such that these ignored bits are 0). + -- + -- For the BBC the ROMs start in bank 8 (the first 8 banks are used by + -- the Spectrum project). The particular bank is selected by the sideways + -- ROM paging register, and bank 7 is used for the MOS. Recommended layout + -- is: + -- + -- 0 Sideways + -- 1 Sideways + -- 2 Sideways - SuperMMC (DFS) + -- 3 Sideways - BASIC + -- 4 Not used + -- 5 Not used + -- 6 Not used + -- 7 MOS + ROM_OFFSET : std_logic_vector(7 downto 0) := "00001000" + ); +port ( + -- Clocks + CLOCK_24 : in std_logic_vector(1 downto 0); + CLOCK_27 : in std_logic_vector(1 downto 0); + CLOCK_50 : in std_logic; + EXT_CLOCK : in std_logic; + + -- Switches + SW : in std_logic_vector(9 downto 0); + -- Buttons + KEY : in std_logic_vector(3 downto 0); + + -- 7 segment displays + HEX0 : out std_logic_vector(6 downto 0); + HEX1 : out std_logic_vector(6 downto 0); + HEX2 : out std_logic_vector(6 downto 0); + HEX3 : out std_logic_vector(6 downto 0); + -- Red LEDs + LEDR : out std_logic_vector(9 downto 0); + -- Green LEDs + LEDG : out std_logic_vector(7 downto 0); + + -- VGA + VGA_R : out std_logic_vector(3 downto 0); + VGA_G : out std_logic_vector(3 downto 0); + VGA_B : out std_logic_vector(3 downto 0); + VGA_HS : out std_logic; + VGA_VS : out std_logic; + + -- Serial + UART_RXD : in std_logic; + UART_TXD : out std_logic; + + -- PS/2 Keyboard + PS2_CLK : inout std_logic; + PS2_DAT : inout std_logic; + + -- I2C + I2C_SCLK : inout std_logic; + I2C_SDAT : inout std_logic; + + -- Audio + AUD_XCK : out std_logic; + AUD_BCLK : out std_logic; + AUD_ADCLRCK : out std_logic; + AUD_ADCDAT : in std_logic; + AUD_DACLRCK : out std_logic; + AUD_DACDAT : out std_logic; + + -- SRAM + SRAM_ADDR : out std_logic_vector(17 downto 0); + SRAM_DQ : inout std_logic_vector(15 downto 0); + SRAM_CE_N : out std_logic; + SRAM_OE_N : out std_logic; + SRAM_WE_N : out std_logic; + SRAM_UB_N : out std_logic; + SRAM_LB_N : out std_logic; + + -- SDRAM + DRAM_ADDR : out std_logic_vector(11 downto 0); + DRAM_DQ : inout std_logic_vector(15 downto 0); + DRAM_BA_0 : in std_logic; + DRAM_BA_1 : in std_logic; + DRAM_CAS_N : in std_logic; + DRAM_CKE : in std_logic; + DRAM_CLK : in std_logic; + DRAM_CS_N : in std_logic; + DRAM_LDQM : in std_logic; + DRAM_RAS_N : in std_logic; + DRAM_UDQM : in std_logic; + DRAM_WE_N : in std_logic; + + -- Flash + FL_ADDR : out std_logic_vector(21 downto 0); + FL_DQ : inout std_logic_vector(7 downto 0); + FL_RST_N : out std_logic; + FL_OE_N : out std_logic; + FL_WE_N : out std_logic; + FL_CE_N : out std_logic; + + -- SD card (SPI mode) + SD_nCS : out std_logic; + SD_MOSI : out std_logic; + SD_SCLK : out std_logic; + SD_MISO : in std_logic; + + -- GPIO + GPIO_0 : inout std_logic_vector(35 downto 0); + GPIO_1 : inout std_logic_vector(35 downto 0) + ); +end entity; + +architecture rtl of bbc_micro_de1 is + +------------------------------ +-- PLL (32 MHz master clock) +------------------------------ + +component pll32 IS + PORT + ( + areset : IN STD_LOGIC := '0'; + inclk0 : IN STD_LOGIC := '0'; + c0 : OUT STD_LOGIC ; + locked : OUT STD_LOGIC + ); +end component; + +--------- +-- CPU +--------- + +component T65 is + port( + Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65C816 + Res_n : in std_logic; + Enable : in std_logic; + Clk : in std_logic; + Rdy : in std_logic; + Abort_n : in std_logic; + IRQ_n : in std_logic; + NMI_n : in std_logic; + SO_n : in std_logic; + R_W_n : out std_logic; + Sync : out std_logic; + EF : out std_logic; + MF : out std_logic; + XF : out std_logic; + ML_n : out std_logic; + VP_n : out std_logic; + VDA : out std_logic; + VPA : out std_logic; + A : out std_logic_vector(23 downto 0); + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0) + ); +end component; + +----------------- +-- MC6845 CRTC +----------------- + +component mc6845 is +port ( + CLOCK : in std_logic; + CLKEN : in std_logic; + nRESET : in std_logic; + + -- Bus interface + ENABLE : in std_logic; + R_nW : in std_logic; + RS : in std_logic; + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0); + + -- Display interface + VSYNC : out std_logic; + HSYNC : out std_logic; + DE : out std_logic; + CURSOR : out std_logic; + LPSTB : in std_logic; + + -- Memory interface + MA : out std_logic_vector(13 downto 0); + RA : out std_logic_vector(4 downto 0) + ); +end component; + +------------------------- +-- "VIDPROC" Video ULA +------------------------- + +component vidproc is +port ( + CLOCK : in std_logic; + -- Clock enable qualifies display cycles (interleaved with CPU cycles) + CLKEN : in std_logic; + nRESET : in std_logic; + + -- Clock enable output to CRTC + CLKEN_CRTC : out std_logic; + + -- Bus interface + ENABLE : in std_logic; + A0 : in std_logic; + -- CPU data bus (for register writes) + DI_CPU : in std_logic_vector(7 downto 0); + -- Display RAM data bus (for display data fetch) + DI_RAM : in std_logic_vector(7 downto 0); + + -- Control interface + nINVERT : in std_logic; + DISEN : in std_logic; + CURSOR : in std_logic; + + -- Video in (teletext mode) + R_IN : in std_logic; + G_IN : in std_logic; + B_IN : in std_logic; + + -- Video out + R : out std_logic; + G : out std_logic; + B : out std_logic + ); +end component; + +-------------------------------- +-- SAA5050 Teletext Generator +-------------------------------- + +component saa5050 is +port ( + CLOCK : in std_logic; + -- 6 MHz dot clock enable + CLKEN : in std_logic; + -- Async reset + nRESET : in std_logic; + + -- Character data input (in the bus clock domain) + DI_CLOCK : in std_logic; + DI_CLKEN : in std_logic; + DI : in std_logic_vector(6 downto 0); + + -- Timing inputs + -- General line reset (not used) + GLR : in std_logic; -- /HSYNC + -- Data entry window - high during VSYNC. + -- Resets ROM row counter and drives 'flash' signal + DEW : in std_logic; -- VSYNC + -- Character rounding select - high during even field + CRS : in std_logic; -- FIELD + -- Load output shift register enable - high during active video + LOSE : in std_logic; -- DE + + -- Video out + R : out std_logic; + G : out std_logic; + B : out std_logic; + Y : out std_logic + ); +end component; + +-------------- +-- 6522 VIA +-------------- + +component M6522 is + port ( + + I_RS : in std_logic_vector(3 downto 0); + I_DATA : in std_logic_vector(7 downto 0); + O_DATA : out std_logic_vector(7 downto 0); + O_DATA_OE_L : out std_logic; + + I_RW_L : in std_logic; + I_CS1 : in std_logic; + I_CS2_L : in std_logic; + + O_IRQ_L : out std_logic; -- note, not open drain + -- port a + I_CA1 : in std_logic; + I_CA2 : in std_logic; + O_CA2 : out std_logic; + O_CA2_OE_L : out std_logic; + + I_PA : in std_logic_vector(7 downto 0); + O_PA : out std_logic_vector(7 downto 0); + O_PA_OE_L : out std_logic_vector(7 downto 0); + + -- port b + I_CB1 : in std_logic; + O_CB1 : out std_logic; + O_CB1_OE_L : out std_logic; + + I_CB2 : in std_logic; + O_CB2 : out std_logic; + O_CB2_OE_L : out std_logic; + + I_PB : in std_logic_vector(7 downto 0); + O_PB : out std_logic_vector(7 downto 0); + O_PB_OE_L : out std_logic_vector(7 downto 0); + + -- FIXME: Revisit timing in this component. Does it really need a 4x clock? + I_P2_H : in std_logic; -- high for phase 2 clock ____----__ + RESET_L : in std_logic; + ENA_4 : in std_logic; -- clk enable (4x system clock rate) + CLK : in std_logic + ); +end component; + +----------------------------- +-- PS/2 Keyboard Emulation +----------------------------- + +component keyboard is +port ( + CLOCK : in std_logic; + nRESET : in std_logic; + CLKEN_1MHZ : in std_logic; + + -- PS/2 interface + PS2_CLK : in std_logic; + PS2_DATA : in std_logic; + + -- If 1 then column is incremented automatically at + -- 1 MHz rate + AUTOSCAN : in std_logic; + + COLUMN : in std_logic_vector(3 downto 0); + ROW : in std_logic_vector(2 downto 0); + + -- 1 when currently selected key is down (AUTOSCAN disabled) + KEYPRESS : out std_logic; + -- 1 when any key is down (except row 0) + INT : out std_logic; + -- BREAK key output - 1 when pressed + BREAK_OUT : out std_logic; + + -- DIP switch inputs + DIP_SWITCH : in std_logic_vector(7 downto 0) + ); +end component; + +----------------------------- +-- SN76489 sound generator +----------------------------- + +component sn76489_top is + + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + ready_o : out std_logic; + d_i : in std_logic_vector(0 to 7); + aout_o : out signed(0 to 7) + ); + +end component; + +component i2s_intf is +generic( + mclk_rate : positive := 12000000; + sample_rate : positive := 8000; + preamble : positive := 1; -- I2S + word_length : positive := 16 + ); + +port ( + -- 2x MCLK in (e.g. 24 MHz for WM8731 USB mode) + CLK : in std_logic; + nRESET : in std_logic; + + -- Parallel IO + PCM_INL : out std_logic_vector(word_length - 1 downto 0); + PCM_INR : out std_logic_vector(word_length - 1 downto 0); + PCM_OUTL : in std_logic_vector(word_length - 1 downto 0); + PCM_OUTR : in std_logic_vector(word_length - 1 downto 0); + + -- Codec interface (right justified mode) + -- MCLK is generated at half of the CLK input + I2S_MCLK : out std_logic; + -- LRCLK is equal to the sample rate and is synchronous to + -- MCLK. It must be related to MCLK by the oversampling ratio + -- given in the codec datasheet. + I2S_LRCLK : out std_logic; + + -- Data is shifted out on the falling edge of BCLK, sampled + -- on the rising edge. The bit rate is determined such that + -- it is fast enough to fit preamble + word_length bits into + -- each LRCLK half cycle. The last cycle of each word may be + -- stretched to fit to LRCLK. This is OK at least for the + -- WM8731 codec. + -- The first falling edge of each timeslot is always synchronised + -- with the LRCLK edge. + I2S_BCLK : out std_logic; + -- Output bitstream + I2S_DOUT : out std_logic; + -- Input bitstream + I2S_DIN : in std_logic + ); +end component; + +component i2c_loader is +generic ( + -- Address of slave to be loaded + device_address : integer := 16#1a#; + -- Number of retries to allow before stopping + num_retries : integer := 0; + -- Length of clock divider in bits. Resulting bus frequency is + -- CLK/2^(log2_divider + 2) + log2_divider : integer := 6 +); + +port ( + CLK : in std_logic; + nRESET : in std_logic; + + I2C_SCL : inout std_logic; + I2C_SDA : inout std_logic; + + IS_DONE : out std_logic; + IS_ERROR : out std_logic + ); +end component; + +component debugger is +generic ( + -- Set this for a reasonable half flash duration relative to the + -- clock frequency + flash_divider : natural := 24 + ); +port ( + CLOCK : in std_logic; + nRESET : in std_logic; + -- CPU clock enable in + CLKEN_IN : in std_logic; + -- Gated clock enable back out to CPU + CLKEN_OUT : out std_logic; + -- CPU IRQ in + nIRQ_IN : in std_logic; + -- Gated IRQ back out to CPU (no interrupts when single stepping) + nIRQ_OUT : out std_logic; + + -- CPU + A_CPU : in std_logic_vector(15 downto 0); + R_nW : in std_logic; + SYNC : in std_logic; + + -- Aux bus input for display in hex + AUX_BUS : in std_logic_vector(15 downto 0); + + -- Controls + -- RUN or HALT CPU + RUN : in std_logic; + -- Push button to single-step in HALT mode + nSTEP : in std_logic; + -- Push button to cycle display mode + nMODE : in std_logic; + -- Push button to cycle display digit in edit mode + nDIGIT : in std_logic; + -- Push button to cycle digit value in edit mode + nSET : in std_logic; + + -- Output to display + DIGIT3 : out std_logic_vector(6 downto 0); + DIGIT2 : out std_logic_vector(6 downto 0); + DIGIT1 : out std_logic_vector(6 downto 0); + DIGIT0 : out std_logic_vector(6 downto 0); + + LED_BREAKPOINT : out std_logic; + LED_WATCHPOINT : out std_logic + ); +end component; + +------------- +-- Signals +------------- + +-- Master clock - 32 MHz +signal pll_reset : std_logic; +signal pll_locked : std_logic; +signal clock : std_logic; +signal hard_reset_n : std_logic; +signal reset_n : std_logic; + +-- Clock enable counter +-- CPU and video cycles are interleaved. The CPU runs at 2 MHz (every 16th +-- cycle) and the video subsystem is enabled on every odd cycle. +signal clken_counter : unsigned(4 downto 0); +signal cpu_cycle : std_logic; -- Qualifies all 2 MHz cycles +signal cpu_cycle_mask : std_logic; -- Set to mask CPU cycles until 1 MHz cycle is complete +signal cpu_clken : std_logic; -- 2 MHz cycles in which the CPU is enabled +signal cpu_debug_clken : std_logic; -- CPU clken return from hardware debugger +-- IO cycles are out of phase with the CPU +signal vid_clken : std_logic; -- 16 MHz video cycles +signal mhz4_clken : std_logic; -- Used by 6522 +signal mhz2_clken : std_logic; -- Used for latching CPU address for clock stretch +signal mhz1_clken : std_logic; -- 1 MHz bus and associated peripherals, 6522 phase 2 +-- SAA5050 needs a 6 MHz clock enable relative to a 24 MHz clock +signal ttxt_clken_counter : unsigned(1 downto 0); +signal ttxt_clken : std_logic; + +-- Debugger connections +signal debug_irq_in_n : std_logic; +signal debug_aux : std_logic_vector(15 downto 0); + +-- CPU signals +signal cpu_mode : std_logic_vector(1 downto 0); +signal cpu_ready : std_logic; +signal cpu_abort_n : std_logic; +signal cpu_irq_n : std_logic; +signal cpu_nmi_n : std_logic; +signal cpu_so_n : std_logic; +signal cpu_r_nw : std_logic; +signal cpu_sync : std_logic; +signal cpu_ef : std_logic; +signal cpu_mf : std_logic; +signal cpu_xf : std_logic; +signal cpu_ml_n : std_logic; +signal cpu_vp_n : std_logic; +signal cpu_vda : std_logic; +signal cpu_vpa : std_logic; +signal cpu_a : std_logic_vector(23 downto 0); +signal cpu_di : std_logic_vector(7 downto 0); +signal cpu_do : std_logic_vector(7 downto 0); + +-- CRTC signals +signal crtc_clken : std_logic; +signal crtc_do : std_logic_vector(7 downto 0); +signal crtc_vsync : std_logic; +signal crtc_hsync : std_logic; +signal crtc_de : std_logic; +signal crtc_cursor : std_logic; +signal crtc_lpstb : std_logic := '0'; +signal crtc_ma : std_logic_vector(13 downto 0); +signal crtc_ra : std_logic_vector(4 downto 0); + +-- Decoded display address after address translation for hardware +-- scrolling +signal display_a : std_logic_vector(14 downto 0); + +-- "VIDPROC" signals +signal vidproc_invert_n : std_logic; +signal vidproc_disen : std_logic; +signal r_in : std_logic; +signal g_in : std_logic; +signal b_in : std_logic; +signal r_out : std_logic; +signal g_out : std_logic; +signal b_out : std_logic; + +-- SAA5050 signals +signal ttxt_glr : std_logic; +signal ttxt_dew : std_logic; +signal ttxt_crs : std_logic; +signal ttxt_lose : std_logic; +signal ttxt_r : std_logic; +signal ttxt_g : std_logic; +signal ttxt_b : std_logic; +signal ttxt_y : std_logic; + +-- System VIA signals +signal sys_via_do : std_logic_vector(7 downto 0); +signal sys_via_do_oe_n : std_logic; +signal sys_via_irq_n : std_logic; +signal sys_via_ca1_in : std_logic := '0'; +signal sys_via_ca2_in : std_logic := '0'; +signal sys_via_ca2_out : std_logic; +signal sys_via_ca2_oe_n : std_logic; +signal sys_via_pa_in : std_logic_vector(7 downto 0); +signal sys_via_pa_out : std_logic_vector(7 downto 0); +signal sys_via_pa_oe_n : std_logic_vector(7 downto 0); +signal sys_via_cb1_in : std_logic := '0'; +signal sys_via_cb1_out : std_logic; +signal sys_via_cb1_oe_n : std_logic; +signal sys_via_cb2_in : std_logic := '0'; +signal sys_via_cb2_out : std_logic; +signal sys_via_cb2_oe_n : std_logic; +signal sys_via_pb_in : std_logic_vector(7 downto 0); +signal sys_via_pb_out : std_logic_vector(7 downto 0); +signal sys_via_pb_oe_n : std_logic_vector(7 downto 0); + +-- User VIA signals +signal user_via_do : std_logic_vector(7 downto 0); +signal user_via_do_oe_n : std_logic; +signal user_via_irq_n : std_logic; +signal user_via_ca1_in : std_logic := '0'; +signal user_via_ca2_in : std_logic := '0'; +signal user_via_ca2_out : std_logic; +signal user_via_ca2_oe_n : std_logic; +signal user_via_pa_in : std_logic_vector(7 downto 0); +signal user_via_pa_out : std_logic_vector(7 downto 0); +signal user_via_pa_oe_n : std_logic_vector(7 downto 0); +signal user_via_cb1_in : std_logic := '0'; +signal user_via_cb1_out : std_logic; +signal user_via_cb1_oe_n : std_logic; +signal user_via_cb2_in : std_logic := '0'; +signal user_via_cb2_out : std_logic; +signal user_via_cb2_oe_n : std_logic; +signal user_via_pb_in : std_logic_vector(7 downto 0); +signal user_via_pb_out : std_logic_vector(7 downto 0); +signal user_via_pb_oe_n : std_logic_vector(7 downto 0); + +-- IC32 latch on System VIA +signal ic32 : std_logic_vector(7 downto 0); +signal sound_enable_n : std_logic; +signal speech_read_n : std_logic; +signal speech_write_n : std_logic; +signal keyb_enable_n : std_logic; +signal disp_addr_offs : std_logic_vector(1 downto 0); +signal caps_lock_led_n : std_logic; +signal shift_lock_led_n : std_logic; + +-- Keyboard +signal keyb_column : std_logic_vector(3 downto 0); +signal keyb_row : std_logic_vector(2 downto 0); +signal keyb_out : std_logic; +signal keyb_int : std_logic; +signal keyb_break : std_logic; + +-- Sound generator +signal sound_ready : std_logic; +signal sound_di : std_logic_vector(7 downto 0); +signal sound_ao : signed(7 downto 0); +signal pcm_inl : std_logic_vector(15 downto 0); +signal pcm_inr : std_logic_vector(15 downto 0); + +-- Memory enables +signal ram_enable : std_logic; -- 0x0000 +signal rom_enable : std_logic; -- 0x8000 (BASIC/sideways ROMs) +signal mos_enable : std_logic; -- 0xC000 +-- IO region enables +signal io_fred : std_logic; -- 0xFC00 (1 MHz bus) +signal io_jim : std_logic; -- 0xFD00 (1 MHz bus) +signal io_sheila : std_logic; -- 0xFE00 (System peripherals) +-- SHIELA +signal crtc_enable : std_logic; -- 0xFE00-FE07 +signal acia_enable : std_logic; -- 0xFE08-FE0F +signal serproc_enable : std_logic; -- 0xFE10-FE1F +signal vidproc_enable : std_logic; -- 0xFE20-FE2F +signal romsel_enable : std_logic; -- 0xFE30-FE3F +signal sys_via_enable : std_logic; -- 0xFE40-FE5F +signal user_via_enable : std_logic; -- 0xFE60-FE7F +signal fddc_enable : std_logic; -- 0xFE80-FE9F +signal adlc_enable : std_logic; -- 0xFEA0-FEBF (Econet) +signal adc_enable : std_logic; -- 0xFEC0-FEDF +signal tube_enable : std_logic; -- 0xFEE0-FEFF + +-- ROM select latch +signal romsel : std_logic_vector(3 downto 0); + +signal mhz1_enable : std_logic; -- Set for access to any 1 MHz peripheral + +begin + ------------------------- + -- COMPONENT INSTANCES + ------------------------- + + -- 32 MHz master clock + pll: pll32 port map ( + pll_reset, + CLOCK_24(0), + clock, + pll_locked ); + + -- Hardware debugger block (single-step, breakpoints) + debug: debugger port map ( + clock, + hard_reset_n, + cpu_clken, + cpu_debug_clken, + debug_irq_in_n, + cpu_irq_n, + cpu_a(15 downto 0), cpu_r_nw, cpu_sync, + debug_aux, + SW(8), -- RUN + KEY(3), -- STEP + KEY(2), -- MODE + KEY(1), -- DIGIT + KEY(0), -- SET + HEX3, HEX2, HEX1, HEX0, + LEDR(3), -- BREAKPOINT + LEDR(2) -- WATCHPOINT + ); + + -- 6502 CPU + cpu : T65 port map ( + cpu_mode, + reset_n, + cpu_debug_clken, + clock, + cpu_ready, + cpu_abort_n, + cpu_irq_n, + cpu_nmi_n, + cpu_so_n, + cpu_r_nw, + cpu_sync, + cpu_ef, + cpu_mf, + cpu_xf, + cpu_ml_n, + cpu_vp_n, + cpu_vda, + cpu_vpa, + cpu_a, + cpu_di, + cpu_do ); + + crtc : mc6845 port map ( + clock, + crtc_clken, + reset_n, + crtc_enable, + cpu_r_nw, + cpu_a(0), + cpu_do, + crtc_do, + crtc_vsync, + crtc_hsync, + crtc_de, + crtc_cursor, + crtc_lpstb, + crtc_ma, + crtc_ra ); + + video_ula : vidproc port map ( + clock, + vid_clken, + reset_n, + crtc_clken, + vidproc_enable, + cpu_a(0), + cpu_do, + SRAM_DQ(7 downto 0), + vidproc_invert_n, + vidproc_disen, + crtc_cursor, + r_in, g_in, b_in, + r_out, g_out, b_out + ); + + teletext : saa5050 port map ( + CLOCK_24(0), -- This runs at 6 MHz, which we can't derive from the 32 MHz clock + ttxt_clken, + reset_n, + clock, -- Data input is synchronised from the bus clock domain + vid_clken, + SRAM_DQ(6 downto 0), + ttxt_glr, + ttxt_dew, + ttxt_crs, + ttxt_lose, + ttxt_r, ttxt_g, ttxt_b, ttxt_y + ); + + -- System VIA + system_via : m6522 port map ( + cpu_a(3 downto 0), + cpu_do, + sys_via_do, + sys_via_do_oe_n, + cpu_r_nw, + sys_via_enable, + '0', -- nCS2 + sys_via_irq_n, + sys_via_ca1_in, + sys_via_ca2_in, + sys_via_ca2_out, + sys_via_ca2_oe_n, + sys_via_pa_in, + sys_via_pa_out, + sys_via_pa_oe_n, + sys_via_cb1_in, + sys_via_cb1_out, + sys_via_cb1_oe_n, + sys_via_cb2_in, + sys_via_cb2_out, + sys_via_cb2_oe_n, + sys_via_pb_in, + sys_via_pb_out, + sys_via_pb_oe_n, + mhz1_clken, + hard_reset_n, -- System VIA is reset by power on reset only + mhz4_clken, + clock + ); + + -- User VIA + user_via : m6522 port map ( + cpu_a(3 downto 0), + cpu_do, + user_via_do, + user_via_do_oe_n, + cpu_r_nw, + user_via_enable, + '0', -- nCS2 + user_via_irq_n, + user_via_ca1_in, + user_via_ca2_in, + user_via_ca2_out, + user_via_ca2_oe_n, + user_via_pa_in, + user_via_pa_out, + user_via_pa_oe_n, + user_via_cb1_in, + user_via_cb1_out, + user_via_cb1_oe_n, + user_via_cb2_in, + user_via_cb2_out, + user_via_cb2_oe_n, + user_via_pb_in, + user_via_pb_out, + user_via_pb_oe_n, + mhz1_clken, + reset_n, + mhz4_clken, + clock + ); + + -- Keyboard + keyb : keyboard port map ( + clock, hard_reset_n, mhz1_clken, + PS2_CLK, PS2_DAT, + keyb_enable_n, + keyb_column, + keyb_row, + keyb_out, + keyb_int, + keyb_break, + SW(7 downto 0) + ); + + -- Sound generator (and drive logic for I2S codec) + sound : sn76489_top port map ( + clock, mhz4_clken, + reset_n, '0', sound_enable_n, + sound_ready, sound_di, + sound_ao + ); + i2s : i2s_intf port map ( + CLOCK_24(0), reset_n, + pcm_inl, pcm_inr, + std_logic_vector(sound_ao) & "00000000", + std_logic_vector(sound_ao) & "00000000", + AUD_XCK, AUD_DACLRCK, + AUD_BCLK, AUD_DACDAT, AUD_ADCDAT + ); + i2c : i2c_loader + generic map ( + log2_divider => 7 + ) + port map ( + clock, reset_n, + I2C_SCLK, I2C_SDAT, + LEDR(5), -- IS_DONE + LEDR(4) -- IS_ERROR + ); + + -- Asynchronous reset + -- PLL is reset by external reset switch + pll_reset <= not SW(9); + -- Keyboard and System VIA are reset by external reset switch or PLL being out of lock + hard_reset_n <= not (pll_reset or not pll_locked); + -- Rest of system is reset by all of the above plus the keyboard BREAK key + reset_n <= hard_reset_n and not keyb_break; + + -- Clock enable generation - 32 MHz clock split into 32 cycles + -- CPU is on 0 and 16 (but can be masked by 1 MHz bus accesses) + -- Video is on all odd cycles (16 MHz) + -- 1 MHz cycles are on cycle 31 (1 MHz) + vid_clken <= clken_counter(0); -- 1,3,5... + mhz4_clken <= clken_counter(0) and clken_counter(1) and clken_counter(2); -- 7/15/23/31 + mhz2_clken <= mhz4_clken and clken_counter(3); -- 15/31 + mhz1_clken <= mhz2_clken and clken_counter(4); -- 31 + cpu_cycle <= not (clken_counter(0) or clken_counter(1) or clken_counter(2) or clken_counter(3)); -- 0/16 + cpu_clken <= cpu_cycle and not cpu_cycle_mask; + + clk_gen: process(clock,reset_n) + begin + if reset_n = '0' then + clken_counter <= (others => '0'); + elsif rising_edge(clock) then + clken_counter <= clken_counter + 1; + end if; + end process; + + cycle_stretch: process(clock,reset_n) + begin + if reset_n = '0' then + cpu_cycle_mask <= '0'; + elsif rising_edge(clock) and mhz2_clken = '1' then + if mhz1_enable = '1' and cpu_cycle_mask = '0' then + -- Block CPU cycles until 1 MHz cycle has completed + cpu_cycle_mask <= '1'; + end if; + if mhz1_clken = '1' then + -- CPU can run again + -- FIXME: This may not be correct in terms of CPU cycles, but it + -- should work + cpu_cycle_mask <= '0'; + end if; + end if; + end process; + + ttxt_clk_gen: process(CLOCK_24(0),reset_n) + begin + if reset_n = '0' then + ttxt_clken_counter <= (others => '0'); + elsif rising_edge(CLOCK_24(0)) then + ttxt_clken_counter <= ttxt_clken_counter + 1; + end if; + end process; + + -- 6 MHz clock enable for SAA5050 + ttxt_clken <= '1' when ttxt_clken_counter = 0 else '0'; + + -- CPU configuration and fixed signals + cpu_mode <= "00"; -- 6502 + cpu_ready <= '1'; + cpu_abort_n <= '1'; + cpu_nmi_n <= '1'; + cpu_so_n <= '1'; + + -- Address decoding + -- 0x0000 = 32 KB SRAM + -- 0x8000 = 16 KB BASIC/Sideways ROMs + -- 0xC000 = 16 KB MOS ROM + -- + -- IO regions are mapped into a hole in the MOS. There are three regions: + -- 0xFC00 = FRED + -- 0xFD00 = JIM + -- 0xFE00 = SHEILA + ram_enable <= not cpu_a(15); + rom_enable <= cpu_a(15) and not cpu_a(14); + mos_enable <= cpu_a(15) and cpu_a(14) and not (io_fred or io_jim or io_sheila); + io_fred <= '1' when cpu_a(15 downto 8) = "11111100" else '0'; + io_jim <= '1' when cpu_a(15 downto 8) = "11111101" else '0'; + io_sheila <= '1' when cpu_a(15 downto 8) = "11111110" else '0'; + -- The following IO regions are accessed at 1 MHz and hence will stall the + -- CPU accordingly + mhz1_enable <= io_fred or io_jim or + adc_enable or sys_via_enable or user_via_enable or + serproc_enable or acia_enable or crtc_enable; + + -- SHEILA address demux + -- All the system peripherals are mapped into this page as follows: + -- 0xFE00 - 0xFE07 = MC6845 CRTC + -- 0xFE08 - 0xFE0F = MC6850 ACIA (Serial/Tape) + -- 0xFE10 - 0xFE1F = Serial ULA + -- 0xFE20 - 0xFE2F = Video ULA + -- 0xFE30 - 0xFE3F = Paged ROM select latch + -- 0xFE40 - 0xFE5F = System VIA (6522) + -- 0xFE60 - 0xFE7F = User VIA (6522) + -- 0xFE80 - 0xFE9F = 8271 Floppy disc controller + -- 0xFEA0 - 0xFEBF = 68B54 ADLC for Econet + -- 0xFEC0 - 0xFEDF = uPD7002 ADC + -- 0xFEE0 - 0xFEFF = Tube ULA + process(cpu_a,io_sheila) + begin + -- All regions normally de-selected + crtc_enable <= '0'; + acia_enable <= '0'; + serproc_enable <= '0'; + vidproc_enable <= '0'; + romsel_enable <= '0'; + sys_via_enable <= '0'; + user_via_enable <= '0'; + fddc_enable <= '0'; + adlc_enable <= '0'; + adc_enable <= '0'; + tube_enable <= '0'; + + if io_sheila = '1' then + case cpu_a(7 downto 5) is + when "000" => + -- 0xFE00 + if cpu_a(4) = '0' then + if cpu_a(3) = '0' then + -- 0xFE00 + crtc_enable <= '1'; + else + -- 0xFE08 + acia_enable <= '1'; + end if; + else + -- 0xFE10 + serproc_enable <= '1'; + end if; + when "001" => + -- 0xFE20 + if cpu_a(4) = '0' then + -- 0xFE20 + vidproc_enable <= '1'; + else + -- 0xFE30 + romsel_enable <= '1'; + end if; + when "010" => sys_via_enable <= '1'; -- 0xFE40 + when "011" => user_via_enable <= '1'; -- 0xFE60 + when "100" => fddc_enable <= '1'; -- 0xFE80 + when "101" => adlc_enable <= '1'; -- 0xFEA0 + when "110" => adc_enable <= '1'; -- 0xFEC0 + when "111" => tube_enable <= '1'; -- 0xFEE0 + when others => + null; + end case; + end if; + end process; + + -- CPU data bus mux and interrupts + cpu_di <= + SRAM_DQ(7 downto 0) when ram_enable = '1' else + FL_DQ when rom_enable = '1' else + FL_DQ when mos_enable = '1' else + crtc_do when crtc_enable = '1' else + "00000010" when acia_enable = '1' else + sys_via_do when sys_via_enable = '1' else + user_via_do when user_via_enable = '1' else + (others => '0'); -- un-decoded locations are pulled down by RP1 + debug_irq_in_n <= sys_via_irq_n and user_via_irq_n; -- route IRQ through debugger + --cpu_irq_n <= sys_via_irq_n and user_via_irq_n; + + -- ROMs are in external flash and split into 16K slots (since this also suits other + -- computers that might be run on the same board). + -- The first 8 slots are allocated for use here, and the first 4 are decoded as + -- the sideways ROMs. Slot 7 is used for the MOS. + FL_RST_N <= reset_n; + FL_CE_N <= '0'; + FL_OE_N <= '0'; + FL_WE_N <= '1'; + FL_ADDR(21 downto 17) <= ROM_OFFSET(7 downto 3); + FL_ADDR(16 downto 14) <= + "111" when mos_enable = '1' else + "0" & romsel(1 downto 0); + FL_ADDR(13 downto 0) <= cpu_a(13 downto 0); + + -- SRAM bus + SRAM_UB_N <= '1'; + SRAM_LB_N <= '0'; + SRAM_CE_N <= '0'; + SRAM_OE_N <= '0'; + SRAM_DQ(15 downto 8) <= (others => '0'); + + -- Synchronous outputs to SRAM + process(clock,reset_n) + variable ram_write : std_logic; + begin + ram_write := ram_enable and not cpu_r_nw; + + if reset_n = '0' then + SRAM_WE_N <= '1'; + SRAM_DQ(7 downto 0) <= (others => 'Z'); + elsif rising_edge(clock) then + -- Default to inputs + SRAM_DQ(7 downto 0) <= (others => 'Z'); + + -- Register SRAM signals to outputs (clock must be at least 2x CPU clock) + if vid_clken = '1' then + -- Fetch data from previous CPU cycle + SRAM_WE_N <= not ram_write; + SRAM_ADDR <= "00" & cpu_a(15 downto 0); + if ram_write = '1' then + SRAM_DQ(7 downto 0) <= cpu_do; + end if; + else + -- Fetch data from previous display cycle + SRAM_WE_N <= '1'; + SRAM_ADDR <= "000" & display_a; + end if; + end if; + end process; + + -- Address translation logic for calculation of display address + process(crtc_ma,crtc_ra,disp_addr_offs) + variable aa : unsigned(3 downto 0); + begin + if crtc_ma(12) = '0' then + -- No adjustment + aa := unsigned(crtc_ma(11 downto 8)); + else + -- Address adjusted according to screen mode to compensate for + -- wrap at 0x8000. + case disp_addr_offs is + when "00" => + -- Mode 3 - restart at 0x4000 + aa := unsigned(crtc_ma(11 downto 8)) + 8; + when "01" => + -- Mode 6 - restart at 0x6000 + aa := unsigned(crtc_ma(11 downto 8)) + 12; + when "10" => + -- Mode 0,1,2 - restart at 0x3000 + aa := unsigned(crtc_ma(11 downto 8)) + 6; + when "11" => + -- Mode 4,5 - restart at 0x5800 + aa := unsigned(crtc_ma(11 downto 8)) + 11; + when others => + null; + end case; + end if; + + if crtc_ma(13) = '0' then + -- HI RES + display_a <= std_logic_vector(aa(3 downto 0)) & crtc_ma(7 downto 0) & crtc_ra(2 downto 0); + else + -- TTX VDU + display_a <= std_logic(aa(3)) & "1111" & crtc_ma(9 downto 0); + end if; + end process; + + -- VIDPROC + vidproc_invert_n <= '1'; + vidproc_disen <= crtc_de and not crtc_ra(3); -- DISEN is masked off by RA(3) for MODEs 3 and 6 + r_in <= ttxt_r; + g_in <= ttxt_g; + b_in <= ttxt_b; + + -- SAA5050 + ttxt_glr <= not crtc_hsync; + ttxt_dew <= crtc_vsync; + ttxt_crs <= not crtc_ra(0); + ttxt_lose <= crtc_de; + + -- CRTC drives video out (CSYNC on HSYNC output, VSYNC high) + VGA_HS <= not (crtc_hsync xor crtc_vsync); + VGA_VS <= '1'; + VGA_R <= r_out & r_out & r_out & r_out; + VGA_G <= g_out & g_out & g_out & g_out; + VGA_B <= b_out & b_out & b_out & b_out; + + -- Connections to System VIA + -- ADC + sys_via_cb1_in <= '1'; -- /EOC + -- CRTC + sys_via_ca1_in <= crtc_vsync; + sys_via_cb2_in <= crtc_lpstb; + -- Keyboard + sys_via_ca2_in <= keyb_int; + sys_via_pa_in(7) <= keyb_out; + sys_via_pa_in(6 downto 0) <= sys_via_pa_out(6 downto 0); -- Must loop back output pins or keyboard won't work + keyb_column <= sys_via_pa_out(3 downto 0); + keyb_row <= sys_via_pa_out(6 downto 4); + -- Sound + sound_di <= sys_via_pa_out; + -- Others (idle until missing bits implemented) + sys_via_pb_in(7 downto 4) <= (others => '1'); + + -- Connections to User VIA (user port is output on green LEDs) + user_via_ca1_in <= '1'; -- Pulled up + --LEDG <= user_via_pb_out; + + -- MMBEEB + user_via_cb1_in <= user_via_pb_out(1); + SD_SCLK <= user_via_pb_out(1); -- SCLK + SD_MOSI <= user_via_pb_out(0); -- SDO + SD_nCS <= '0'; -- CS + user_via_cb2_in <= SD_MISO; -- SDI + user_via_pb_in <= user_via_pb_out; + + -- ROM select latch + process(clock,reset_n) + begin + if reset_n = '0' then + romsel <= (others => '0'); + elsif rising_edge(clock) then + if romsel_enable = '1' and cpu_r_nw = '0' then + romsel <= cpu_do(3 downto 0); + end if; + end if; + end process; + + -- IC32 latch + sound_enable_n <= ic32(0); + speech_write_n <= ic32(1); + speech_read_n <= ic32(2); + keyb_enable_n <= ic32(3); + disp_addr_offs <= ic32(5 downto 4); + caps_lock_led_n <= ic32(6); + shift_lock_led_n <= ic32(7); + + process(clock,reset_n) + variable bit_num : integer; + begin + bit_num := to_integer(unsigned(sys_via_pb_out(2 downto 0))); + + if reset_n = '0' then + ic32 <= (others => '0'); + elsif rising_edge(clock) then + ic32(bit_num) <= sys_via_pb_out(3); + end if; + end process; + + -- Keyboard LEDs + LEDR(0) <= not caps_lock_led_n; + LEDR(1) <= not shift_lock_led_n; + + ----------------- + -- DEBUG STUFF + ----------------- + + GPIO_0(0) <= not (crtc_hsync xor crtc_vsync); + GPIO_0(1) <= crtc_de; + +end architecture; diff --git a/quartus/bbc_micro_de1_tb.vhd b/quartus/bbc_micro_de1_tb.vhd new file mode 100644 index 0000000..82012ec --- /dev/null +++ b/quartus/bbc_micro_de1_tb.vhd @@ -0,0 +1,299 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity bbc_micro_tb is +end entity; + +architecture tb of bbc_micro_tb is +component bbc_micro_de1 is +port ( + -- Clocks + CLOCK_24 : in std_logic_vector(1 downto 0); + CLOCK_27 : in std_logic_vector(1 downto 0); + CLOCK_50 : in std_logic; + EXT_CLOCK : in std_logic; + + -- Switches + SW : in std_logic_vector(9 downto 0); + -- Buttons + KEY : in std_logic_vector(3 downto 0); + + -- 7 segment displays + HEX0 : out std_logic_vector(6 downto 0); + HEX1 : out std_logic_vector(6 downto 0); + HEX2 : out std_logic_vector(6 downto 0); + HEX3 : out std_logic_vector(6 downto 0); + -- Red LEDs + LEDR : out std_logic_vector(9 downto 0); + -- Green LEDs + LEDG : out std_logic_vector(7 downto 0); + + -- VGA + VGA_R : out std_logic_vector(3 downto 0); + VGA_G : out std_logic_vector(3 downto 0); + VGA_B : out std_logic_vector(3 downto 0); + VGA_HS : out std_logic; + VGA_VS : out std_logic; + + -- Serial + UART_RXD : in std_logic; + UART_TXD : out std_logic; + + -- PS/2 Keyboard + PS2_CLK : inout std_logic; + PS2_DAT : inout std_logic; + + -- I2C + I2C_SCLK : inout std_logic; + I2C_SDAT : inout std_logic; + + -- Audio + AUD_XCK : out std_logic; + AUD_BCLK : out std_logic; + AUD_ADCLRCK : out std_logic; + AUD_ADCDAT : in std_logic; + AUD_DACLRCK : out std_logic; + AUD_DACDAT : out std_logic; + + -- SRAM + SRAM_ADDR : out std_logic_vector(17 downto 0); + SRAM_DQ : inout std_logic_vector(15 downto 0); + SRAM_CE_N : out std_logic; + SRAM_OE_N : out std_logic; + SRAM_WE_N : out std_logic; + SRAM_UB_N : out std_logic; + SRAM_LB_N : out std_logic; + + -- SDRAM + DRAM_ADDR : out std_logic_vector(11 downto 0); + DRAM_DQ : inout std_logic_vector(15 downto 0); + DRAM_BA_0 : in std_logic; + DRAM_BA_1 : in std_logic; + DRAM_CAS_N : in std_logic; + DRAM_CKE : in std_logic; + DRAM_CLK : in std_logic; + DRAM_CS_N : in std_logic; + DRAM_LDQM : in std_logic; + DRAM_RAS_N : in std_logic; + DRAM_UDQM : in std_logic; + DRAM_WE_N : in std_logic; + + -- Flash + FL_ADDR : out std_logic_vector(21 downto 0); + FL_DQ : inout std_logic_vector(7 downto 0); + FL_RST_N : in std_logic; + FL_OE_N : in std_logic; + FL_WE_N : in std_logic; + + -- GPIO + GPIO_0 : inout std_logic_vector(35 downto 0); + GPIO_1 : inout std_logic_vector(35 downto 0) + ); +end component; + + +signal clock_24 : std_logic_vector(1 downto 0) := "00"; +signal clock_27 : std_logic_vector(1 downto 0) := "00"; +signal clock_50 : std_logic := '0'; +signal ext_clock : std_logic := '0'; +signal sw : std_logic_vector(9 downto 0); +signal key : std_logic_vector(3 downto 0); +signal hex0 : std_logic_vector(6 downto 0); +signal hex1 : std_logic_vector(6 downto 0); +signal hex2 : std_logic_vector(6 downto 0); +signal hex3 : std_logic_vector(6 downto 0); +signal ledr : std_logic_vector(9 downto 0); +signal ledg : std_logic_vector(7 downto 0); +signal vga_r : std_logic_vector(3 downto 0); +signal vga_g : std_logic_vector(3 downto 0); +signal vga_b : std_logic_vector(3 downto 0); +signal vga_hs : std_logic; +signal vga_vs : std_logic; +signal uart_rxd : std_logic; +signal uart_txd : std_logic; +signal ps2_clk : std_logic; +signal ps2_dat : std_logic; +signal i2c_sclk : std_logic; +signal i2c_sdat : std_logic; +signal aud_xck : std_logic; +signal aud_bclk : std_logic; +signal aud_adclrck : std_logic; +signal aud_adcdat : std_logic; +signal aud_daclrck : std_logic; +signal aud_dacdat : std_logic; +signal sram_addr : std_logic_vector(17 downto 0); +signal sram_dq : std_logic_vector(15 downto 0); +signal sram_ce_n : std_logic; +signal sram_oe_n : std_logic; +signal sram_we_n : std_logic; +signal sram_ub_n : std_logic; +signal sram_lb_n : std_logic; +signal dram_addr : std_logic_vector(11 downto 0); +signal dram_dq : std_logic_vector(15 downto 0); +signal dram_ba_0 : std_logic; +signal dram_ba_1 : std_logic; +signal dram_cas_n : std_logic; +signal dram_cke : std_logic; +signal dram_clk : std_logic; +signal dram_cs_n : std_logic; +signal dram_ldqm : std_logic; +signal dram_ras_n : std_logic; +signal dram_udqm : std_logic; +signal dram_we_n : std_logic; +signal fl_addr : std_logic_vector(21 downto 0); +signal fl_dq : std_logic_vector(7 downto 0); +signal fl_rst_n : std_logic; +signal fl_oe_n : std_logic; +signal fl_we_n : std_logic; +signal gpio_0 : std_logic_vector(35 downto 0); +signal gpio_1 : std_logic_vector(35 downto 0); + +signal n_reset : std_logic := '0'; +signal n_slow : std_logic := '1'; + +type ram_t is array(0 to 65535) of std_logic_vector(15 downto 0); +signal ram : ram_t; +signal ram_a : std_logic_vector(15 downto 0); +begin + + uut: bbc_micro_de1 port map ( + clock_24, + clock_27, + clock_50, + ext_clock, + sw, + key, + hex0, + hex1, + hex2, + hex3, + ledr, + ledg, + vga_r, + vga_g, + vga_b, + vga_hs, + vga_vs, + uart_rxd, + uart_txd, + ps2_clk, + ps2_dat, + i2c_sclk, + i2c_sdat, + aud_xck, + aud_bclk, + aud_adclrck, + aud_adcdat, + aud_daclrck, + aud_dacdat, + sram_addr, + sram_dq, + sram_ce_n, + sram_oe_n, + sram_we_n, + sram_ub_n, + sram_lb_n, + dram_addr, + dram_dq, + dram_ba_0, + dram_ba_1, + dram_cas_n, + dram_cke, + dram_clk, + dram_cs_n, + dram_ldqm, + dram_ras_n, + dram_udqm, + dram_we_n, + fl_addr, + fl_dq, + fl_rst_n, + fl_oe_n, + fl_we_n, + gpio_0, + gpio_1 + ); + + sw <= n_reset & n_slow & "00000101"; + clock_50 <= not clock_50 after 10 ns; + clock_27(0) <= not clock_27(0) after 18.5 ns; + clock_27(1) <= not clock_27(1) after 18.5 ns; + clock_24(0) <= not clock_24(0) after 20.8 ns; + clock_24(1) <= not clock_24(1) after 20.8 ns; + + reset: process + begin + wait for 100 ns; + n_reset <= '1'; + end process; + + sram: process(sram_addr,sram_dq,sram_ce_n,sram_oe_n,sram_we_n,sram_ub_n,sram_lb_n) + begin + if sram_ce_n = '0' then + if sram_oe_n = '0' and sram_we_n = '1' then + if sram_ub_n = '0' then + sram_dq(15 downto 8) <= ram(to_integer(unsigned(sram_addr(15 downto 0))))(15 downto 8); + else + sram_dq(15 downto 8) <= (others => 'Z'); + end if; + if sram_lb_n = '0' then + sram_dq(7 downto 0) <= ram(to_integer(unsigned(sram_addr(15 downto 0))))(7 downto 0); + else + sram_dq(7 downto 0) <= (others => 'Z'); + end if; + else + sram_dq(15 downto 0) <= (others => 'Z'); + if sram_we_n = '0' then + if sram_ub_n = '0' then + ram(to_integer(unsigned(sram_addr(15 downto 0))))(15 downto 8) <= sram_dq(15 downto 8); + end if; + if sram_lb_n = '0' then + ram(to_integer(unsigned(sram_addr(15 downto 0))))(7 downto 0) <= sram_dq(7 downto 0); + end if; + end if; + end if; + else + sram_dq <= (others => 'Z'); + end if; + end process; + + +end architecture; diff --git a/quartus/debugger.vhd b/quartus/debugger.vhd new file mode 100644 index 0000000..1d78bd5 --- /dev/null +++ b/quartus/debugger.vhd @@ -0,0 +1,300 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- +-- General purpose hardware debugger +-- +-- (C) 2011 Mike Stirling +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity debugger is +generic ( + -- Set this for a reasonable half flash duration relative to the + -- clock frequency + flash_divider : natural := 24 + ); +port ( + CLOCK : in std_logic; + nRESET : in std_logic; + -- CPU clock enable in + CLKEN_IN : in std_logic; + -- Gated clock enable back out to CPU + CLKEN_OUT : out std_logic; + -- CPU IRQ in + nIRQ_IN : in std_logic; + -- Gated IRQ back out to CPU (no interrupts when single stepping) + nIRQ_OUT : out std_logic; + + -- CPU + A_CPU : in std_logic_vector(15 downto 0); + R_nW : in std_logic; + SYNC : in std_logic; + + -- Aux bus input for display in hex + AUX_BUS : in std_logic_vector(15 downto 0); + + -- Controls + -- RUN or HALT CPU + RUN : in std_logic; + -- Push button to single-step in HALT mode + nSTEP : in std_logic; + -- Push button to cycle display mode + nMODE : in std_logic; + -- Push button to cycle display digit in edit mode + nDIGIT : in std_logic; + -- Push button to cycle digit value in edit mode + nSET : in std_logic; + + -- Output to display + DIGIT3 : out std_logic_vector(6 downto 0); + DIGIT2 : out std_logic_vector(6 downto 0); + DIGIT1 : out std_logic_vector(6 downto 0); + DIGIT0 : out std_logic_vector(6 downto 0); + + LED_BREAKPOINT : out std_logic; + LED_WATCHPOINT : out std_logic + ); +end entity; + +architecture rtl of debugger is + +component seg7 is +port ( + D : in std_logic_vector(3 downto 0); + Q : out std_logic_vector(6 downto 0) +); +end component; + +-- Current display mode +type mode_t is (modeAddress,modeBreak,modeWatch,modeAux); +signal mode : mode_t; +-- Current edit digit +signal digit : unsigned(1 downto 0); +-- For flashing selected digit +signal counter : unsigned(flash_divider-1 downto 0); +signal flash : std_logic; +-- Selected breakpoint address (stop on instruction fetch) +signal breakpoint : std_logic_vector(15 downto 0); +-- Selected watchpoint address (stop on write) +signal watchpoint : std_logic_vector(15 downto 0); +-- Address of last instruction fetch +signal instr_addr : std_logic_vector(15 downto 0); +-- Break flags +signal halt : std_logic; +-- Set when a request to resume has been received but before +-- the CPU has run +signal resuming : std_logic; + +-- Display interface +signal a_display : std_logic_vector(15 downto 0); +signal d3_display : std_logic_vector(6 downto 0); +signal d2_display : std_logic_vector(6 downto 0); +signal d1_display : std_logic_vector(6 downto 0); +signal d0_display : std_logic_vector(6 downto 0); + +-- Registered button inputs +signal r_step_n : std_logic; +signal r_mode_n : std_logic; +signal r_digit_n : std_logic; +signal r_set_n : std_logic; + +begin + -- Mask CPU clock enable + CLKEN_OUT <= CLKEN_IN and not halt; + -- Mask interrupt + nIRQ_OUT <= nIRQ_IN or not RUN; + + -- Route selected address to display + a_display <= instr_addr when mode = modeAddress else + breakpoint when mode = modeBreak else + watchpoint when mode = modeWatch else + AUX_BUS when mode = modeAux else + (others => '0'); + + -- Generate display digits from binary + d3 : seg7 port map (a_display(15 downto 12),d3_display); + d2 : seg7 port map (a_display(11 downto 8),d2_display); + d1 : seg7 port map (a_display(7 downto 4),d1_display); + d0 : seg7 port map (a_display(3 downto 0),d0_display); + + -- Flash selected digit in edit modes + DIGIT3 <= d3_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "11") else "1111111"; + DIGIT2 <= d2_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "10") else "1111111"; + DIGIT1 <= d1_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "01") else "1111111"; + DIGIT0 <= d0_display when (mode = modeAddress or mode = modeAux or flash = '1' or digit /= "00") else "1111111"; + + -- Show mode on LEDs + LED_BREAKPOINT <= '1' when mode = modeBreak or mode = modeAux else '0'; + LED_WATCHPOINT <= '1' when mode = modeWatch or mode = modeAux else '0'; + + -- Flash counter + process(CLOCK,nRESET) + begin + if nRESET = '0' then + counter <= (others => '0'); + flash <= '0'; + elsif rising_edge(CLOCK) then + counter <= counter + 1; + if counter = 0 then + flash <= not flash; + end if; + end if; + end process; + + -- Register buttons, select input mode and digit + process(CLOCK,nRESET) + begin + if nRESET = '0' then + r_mode_n <= '1'; + r_digit_n <= '1'; + r_set_n <= '1'; + mode <= modeAddress; + digit <= (others => '0'); + elsif rising_edge(CLOCK) then + -- Register buttons + r_mode_n <= nMODE; + r_digit_n <= nDIGIT; + r_set_n <= nSET; + + if r_mode_n = '1' and nMODE = '0' then + -- Increment mode + if mode = modeAddress then + mode <= modeBreak; + elsif mode = modeBreak then + mode <= modeWatch; + elsif mode = modeWatch then + mode <= modeAux; + else + mode <= modeAddress; + end if; + end if; + if r_digit_n = '1' and nDIGIT = '0' then + -- Increment digit + digit <= digit + 1; + end if; + end if; + end process; + + -- Set watchpoint address + process(CLOCK,nRESET) + begin + if nRESET = '0' then + watchpoint <= (others => '1'); + elsif rising_edge(CLOCK) and mode = modeWatch then + if r_set_n = '1' and nSET = '0' then + -- Increment selected digit on each button press + case digit is + when "00" => watchpoint(3 downto 0) <= std_logic_vector(unsigned(watchpoint(3 downto 0)) + 1); + when "01" => watchpoint(7 downto 4) <= std_logic_vector(unsigned(watchpoint(7 downto 4)) + 1); + when "10" => watchpoint(11 downto 8) <= std_logic_vector(unsigned(watchpoint(11 downto 8)) + 1); + when "11" => watchpoint(15 downto 12) <= std_logic_vector(unsigned(watchpoint(15 downto 12)) + 1); + when others => null; + end case; + end if; + end if; + end process; + + -- Set breakpoint address + process(CLOCK,nRESET) + begin + if nRESET = '0' then + breakpoint <= (others => '1'); + elsif rising_edge(CLOCK) and mode = modeBreak then + if r_set_n = '1' and nSET = '0' then + -- Increment selected digit on each button press + case digit is + when "00" => breakpoint(3 downto 0) <= std_logic_vector(unsigned(breakpoint(3 downto 0)) + 1); + when "01" => breakpoint(7 downto 4) <= std_logic_vector(unsigned(breakpoint(7 downto 4)) + 1); + when "10" => breakpoint(11 downto 8) <= std_logic_vector(unsigned(breakpoint(11 downto 8)) + 1); + when "11" => breakpoint(15 downto 12) <= std_logic_vector(unsigned(breakpoint(15 downto 12)) + 1); + + when others => null; + end case; + end if; + end if; + end process; + + -- CPU control logic + process(CLOCK,nRESET) + begin + if nRESET = '0' then + r_step_n <= '1'; + halt <= '0'; + resuming <= '0'; + instr_addr <= (others => '0'); + elsif rising_edge(CLOCK) then + -- Register single-step button + r_step_n <= nSTEP; + + -- Once the CPU has run we can trigger a new halt + if CLKEN_IN = '1' then + resuming <= '0'; + end if; + + if SYNC = '1' then + -- Latch address of instruction fetch + instr_addr <= A_CPU; + end if; + + -- Check for halt conditions if we are not resuming from a previous halt + if resuming = '0' then + if RUN = '0' and SYNC = '1' then + -- If not in RUN mode then halt on any instruction fetch + -- (single-step) + halt <= '1'; + end if; + if A_CPU = breakpoint and SYNC = '1' then + -- Halt CPU when instruction fetched from breakpoint address + halt <= '1'; + end if; + if A_CPU = watchpoint and SYNC = '0' and R_nW = '0' then + -- Halt CPU when data write to watchpoint address + halt <= '1'; + end if; + end if; + + -- Resume or single step when user presses "STEP" button + if r_step_n = '1' and nSTEP = '0' then + resuming <= '1'; + halt <= '0'; + end if; + end if; + end process; +end architecture; diff --git a/quartus/i2c_loader.vhd b/quartus/i2c_loader.vhd new file mode 100644 index 0000000..35da643 --- /dev/null +++ b/quartus/i2c_loader.vhd @@ -0,0 +1,304 @@ +-- ZX Spectrum for Altera DE1 +-- +-- Copyright (c) 2009-2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.STD_LOGIC_ARITH.ALL; +use IEEE.STD_LOGIC_UNSIGNED.ALL; +use IEEE.STD_LOGIC_MISC.ALL; -- for AND_REDUCE +use IEEE.NUMERIC_STD.ALL; + +entity i2c_loader is +generic ( + -- Address of slave to be loaded + device_address : integer := 16#1a#; + -- Number of retries to allow before stopping + num_retries : integer := 0; + -- Length of clock divider in bits. Resulting bus frequency is + -- CLK/2^(log2_divider + 2) + log2_divider : integer := 6 +); + +port ( + CLK : in std_logic; + nRESET : in std_logic; + + I2C_SCL : inout std_logic; + I2C_SDA : inout std_logic; + + IS_DONE : out std_logic; + IS_ERROR : out std_logic + ); +end i2c_loader; + +architecture i2c_loader_arch of i2c_loader is +type regs is array(0 to 19) of std_logic_vector(7 downto 0); +constant init_regs : regs := ( + -- Left line in, 0dB, unmute + X"00", X"17", + -- Right line in, 0dB, unmute + X"02", X"17", + -- Left headphone out, 0dB + X"04", X"79", + -- Right headphone out, 0dB + X"06", X"79", + -- Audio path, DAC enabled, Line in, Bypass off, mic unmuted + X"08", X"10", + -- Digital path, Unmute, HP filter enabled + X"0A", X"00", + -- Power down mic, clkout and xtal osc + X"0C", X"62", + -- Format 16-bit I2S, no bit inversion or phase changes + X"0E", X"02", + -- Sampling control, 8 kHz USB mode (MCLK = 250fs * 6) + X"10", X"0D", + -- Activate + X"12", X"01" + ); +-- Number of bursts (i.e. total number of registers) +constant burst_length : positive := 2; +-- Number of bytes to transfer per burst +constant num_bursts : positive := (init_regs'length / burst_length); + +type state_t is (Idle, Start, Data, Ack, Stop, Pause, Done); +signal state : state_t; +signal phase : std_logic_vector(1 downto 0); +subtype nbit_t is integer range 0 to 7; +signal nbit : nbit_t; +subtype nbyte_t is integer range 0 to burst_length; -- +1 for address byte +signal nbyte : nbyte_t; +subtype thisbyte_t is integer range 0 to init_regs'length; -- +1 for "done" +signal thisbyte : thisbyte_t; +subtype retries_t is integer range 0 to num_retries; +signal retries : retries_t; + +signal clken : std_logic; +signal divider : std_logic_vector(log2_divider-1 downto 0); +signal shiftreg : std_logic_vector(7 downto 0); +signal scl_out : std_logic; +signal sda_out : std_logic; +signal nak : std_logic; +begin + -- Create open-drain outputs for I2C bus + I2C_SCL <= '0' when scl_out = '0' else 'Z'; + I2C_SDA <= '0' when sda_out = '0' else 'Z'; + -- Status outputs are driven both ways + IS_DONE <= '1' when state = Done else '0'; + IS_ERROR <= nak; + + -- Generate clock enable for desired bus speed + clken <= AND_REDUCE(divider); + process(nRESET,CLK) + begin + if nRESET = '0' then + divider <= (others => '0'); + elsif falling_edge(CLK) then + divider <= divider + '1'; + end if; + end process; + + -- The I2C loader process + process(nRESET,CLK) + begin + if nRESET = '0' then + scl_out <= '1'; + sda_out <= '1'; + state <= Idle; + phase <= "00"; + nbit <= 0; + nbyte <= 0; + thisbyte <= 0; + shiftreg <= (others => '0'); + nak <= '0'; -- No error + retries <= num_retries; + elsif rising_edge(CLK) and clken = '1' then + -- Next phase by default + phase <= phase + 1; + + -- STATE: IDLE + if state = Idle then + -- Start loading the device registers straight away + -- A 'GO' bit could be polled here if required + state <= Start; + phase <= "00"; + scl_out <= '1'; + sda_out <= '1'; + + -- STATE: START + elsif state = Start then + -- Generate START condition + case phase is + when "00" => + -- Drop SDA first + sda_out <= '0'; + when "10" => + -- Then drop SCL + scl_out <= '0'; + when "11" => + -- Advance to next state + -- Shift register loaded with device slave address + state <= Data; + nbit <= 7; + shiftreg <= std_logic_vector(to_unsigned(device_address,7)) & '0'; -- writing + nbyte <= burst_length; + when others => + null; + end case; + + -- STATE: DATA + elsif state = Data then + -- Generate data + case phase is + when "00" => + -- Drop SCL + scl_out <= '0'; + when "01" => + -- Output data and shift (MSb first) + sda_out <= shiftreg(7); + shiftreg <= shiftreg(6 downto 0) & '0'; + when "10" => + -- Raise SCL + scl_out <= '1'; + when "11" => + -- Next bit or advance to next state when done + if nbit = 0 then + state <= Ack; + else + nbit <= nbit - 1; + end if; + when others => + null; + end case; + + -- STATE: ACK + elsif state = Ack then + -- Generate ACK clock and check for error condition + case phase is + when "00" => + -- Drop SCL + scl_out <= '0'; + when "01" => + -- Float data + sda_out <= '1'; + when "10" => + -- Sample ack bit + nak <= I2C_SDA; + if I2C_SDA = '1' then + -- Error + nbyte <= 0; -- Close this burst and skip remaining registers + thisbyte <= init_regs'length; + else + -- Hold ACK to avoid spurious stops - this seems to fix a + -- problem with the Wolfson codec which releases the ACK + -- right on the falling edge of the clock pulse. It looks like + -- the device interprets this is a STOP condition and then fails + -- to acknowledge the next byte. We can avoid this by holding the + -- ACK condition for a little longer. + sda_out <= '0'; + end if; + -- Raise SCL + scl_out <= '1'; + when "11" => + -- Advance to next state + if nbyte = 0 then + -- No more bytes in this burst - generate a STOP + state <= Stop; + else + -- Generate next byte + state <= Data; + nbit <= 7; + shiftreg <= init_regs(thisbyte); + nbyte <= nbyte - 1; + thisbyte <= thisbyte + 1; + end if; + when others => + null; + end case; + + -- STATE: STOP + elsif state = Stop then + -- Generate STOP condition + case phase is + when "00" => + -- Drop SCL first + scl_out <= '0'; + when "01" => + -- Drop SDA + sda_out <= '0'; + when "10" => + -- Raise SCL + scl_out <= '1'; + when "11" => + if thisbyte = init_regs'length then + -- All registers done, advance to finished state. This will + -- bring SDA high while SCL is still high, completing the STOP + -- condition + state <= Done; + else + -- Load the next register after a short delay + state <= Pause; + end if; + when others => + null; + end case; + + -- STATE: PAUSE + elsif state = Pause then + -- Delay for one cycle of 'phase' then start the next burst + scl_out <= '1'; + sda_out <= '1'; + if phase = "11" then + state <= Start; + end if; + + -- STATE: DONE + else + -- Finished + scl_out <= '1'; + sda_out <= '1'; + + if nak = '1' and retries > 0 then + -- We can retry in the event of a NAK in case the + -- slave got out of sync for some reason + retries <= retries - 1; + state <= Idle; + end if; + end if; + end if; + end process; +end i2c_loader_arch; + diff --git a/quartus/i2s_intf.vhd b/quartus/i2s_intf.vhd new file mode 100644 index 0000000..e15ce2c --- /dev/null +++ b/quartus/i2s_intf.vhd @@ -0,0 +1,195 @@ +-- ZX Spectrum for Altera DE1 +-- +-- Copyright (c) 2009-2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.STD_LOGIC_ARITH.ALL; +use IEEE.STD_LOGIC_UNSIGNED.ALL; + +entity i2s_intf is +generic( + mclk_rate : positive := 12000000; + sample_rate : positive := 8000; + preamble : positive := 1; -- I2S + word_length : positive := 16 + ); + +port ( + -- 2x MCLK in (e.g. 24 MHz for WM8731 USB mode) + CLK : in std_logic; + nRESET : in std_logic; + + -- Parallel IO + PCM_INL : out std_logic_vector(word_length - 1 downto 0); + PCM_INR : out std_logic_vector(word_length - 1 downto 0); + PCM_OUTL : in std_logic_vector(word_length - 1 downto 0); + PCM_OUTR : in std_logic_vector(word_length - 1 downto 0); + + -- Codec interface (right justified mode) + -- MCLK is generated at half of the CLK input + I2S_MCLK : out std_logic; + -- LRCLK is equal to the sample rate and is synchronous to + -- MCLK. It must be related to MCLK by the oversampling ratio + -- given in the codec datasheet. + I2S_LRCLK : out std_logic; + + -- Data is shifted out on the falling edge of BCLK, sampled + -- on the rising edge. The bit rate is determined such that + -- it is fast enough to fit preamble + word_length bits into + -- each LRCLK half cycle. The last cycle of each word may be + -- stretched to fit to LRCLK. This is OK at least for the + -- WM8731 codec. + -- The first falling edge of each timeslot is always synchronised + -- with the LRCLK edge. + I2S_BCLK : out std_logic; + -- Output bitstream + I2S_DOUT : out std_logic; + -- Input bitstream + I2S_DIN : in std_logic + ); +end i2s_intf; + +architecture i2s_intf_arch of i2s_intf is +constant ratio_mclk_fs : positive := (mclk_rate / sample_rate); +constant lrdivider_top : positive := (ratio_mclk_fs / 2) - 1; +constant bdivider_top : positive := (ratio_mclk_fs / 8 / (preamble + word_length) * 2) - 1; +constant nbits : positive := preamble + word_length; + +subtype lrdivider_t is integer range 0 to lrdivider_top; +subtype bdivider_t is integer range 0 to bdivider_top; +subtype bitcount_t is integer range 0 to nbits; + +signal lrdivider : lrdivider_t; +signal bdivider : bdivider_t; +signal bitcount : bitcount_t; + +signal mclk_r : std_logic; +signal lrclk_r : std_logic; +signal bclk_r : std_logic; + +-- Shift register is long enough for the number of data bits +-- plus the preamble, plus an extra bit on the right to register +-- the incoming data +signal shiftreg : std_logic_vector(nbits downto 0); +begin + I2S_MCLK <= mclk_r; + I2S_LRCLK <= lrclk_r; + I2S_BCLK <= bclk_r; + I2S_DOUT <= shiftreg(nbits); -- data goes out MSb first + + process(nRESET,CLK) + begin + if nRESET = '0' then + PCM_INL <= (others => '0'); + PCM_INR <= (others => '0'); + + -- Preload down-counters for clock generation + lrdivider <= lrdivider_top; + bdivider <= bdivider_top; + bitcount <= nbits; + + mclk_r <= '0'; + lrclk_r <= '0'; + bclk_r <= '0'; + shiftreg <= (others => '0'); + elsif rising_edge(CLK) then + -- Generate MCLK at half input clock rate + mclk_r <= not mclk_r; + + -- Generate LRCLK at rate specified by codec configuration + if lrdivider = 0 then + -- LRCLK divider has reached 0 - start again from the top + lrdivider <= lrdivider_top; + + -- Generate LRCLK edge and sync the BCLK counter + lrclk_r <= not lrclk_r; + bclk_r <= '0'; + bitcount <= nbits; -- 1 extra required for setup + bdivider <= bdivider_top; + + -- Load shift register with output data padding preamble + -- with 0s. Load output buses with input word from the + -- previous timeslot. + shiftreg(nbits downto nbits - preamble + 1) <= (others => '0'); + if lrclk_r = '0' then + -- Previous channel input is LEFT. This is available in the + -- shift register at the end of a cycle, right justified + PCM_INL <= shiftreg(word_length - 1 downto 0); + -- Next channel to output is RIGHT. Load this into the + -- shift register at the start of a cycle, left justified + shiftreg(word_length downto 1) <= PCM_OUTR; + else + -- Previous channel input is RIGHT + PCM_INR <= shiftreg(word_length - 1 downto 0); + -- Next channel is LEFT + shiftreg(word_length downto 1) <= PCM_OUTL; + end if; + else + -- Decrement the LRCLK counter + lrdivider <= lrdivider - 1; + + -- Generate BCLK at a suitable rate to fit the required number + -- of bits into each timeslot. Data is changed on the falling edge, + -- sampled on the rising edge + if bdivider = 0 then + -- If all bits have been output for this phase then + -- stop and wait to sync back up with LRCLK + if bitcount > 0 then + -- Reset + bdivider <= bdivider_top; + + -- Toggle BCLK + bclk_r <= not bclk_r; + if bclk_r = '0' then + -- Rising edge - shift in current bit and decrement bit counter + bitcount <= bitcount - 1; + shiftreg(0) <= I2S_DIN; + else + -- Falling edge - shift out next bit + shiftreg(nbits downto 1) <= shiftreg(nbits - 1 downto 0); + end if; + end if; + else + -- Decrement the BCLK counter + bdivider <= bdivider - 1; + end if; + end if; + + end if; + end process; +end i2s_intf_arch; + diff --git a/quartus/keyboard.odg b/quartus/keyboard.odg new file mode 100644 index 0000000..403b1e7 Binary files /dev/null and b/quartus/keyboard.odg differ diff --git a/quartus/keyboard.png b/quartus/keyboard.png new file mode 100644 index 0000000..12735bd Binary files /dev/null and b/quartus/keyboard.png differ diff --git a/quartus/keyboard.txt b/quartus/keyboard.txt new file mode 100644 index 0000000..d63fa03 --- /dev/null +++ b/quartus/keyboard.txt @@ -0,0 +1,81 @@ +Column Row Key PC key Scancode +0 0 SHIFT LEFT SHIFT, RIGHT SHIFT 12 + 59 +0 1 Q Q 15 +0 2 F0 F10 09 +0 3 1 (!) 1 16 +0 4 CAPS LOCK CAPS LOCK 58 +0 5 SHIFT LOCK LEFT ALT 11 +0 6 TAB TAB 0D +0 7 ESCAPE ESCAPE 76 +1 0 CTRL LEFT CTRL, RIGHT CTRL 14 + E0,14 +1 1 3 (#) 3 26 +1 2 W W 1D +1 3 2 (") 2 1E +1 4 A A 1C +1 5 S S 1B +1 6 Z Z 1A +1 7 F1 F1 05 +2 0 DIP 7 +2 1 4 ($) 4 25 +2 2 E E 24 +2 3 D D 23 +2 4 X X 22 +2 5 C C 21 +2 6 SPACE SPACE 29 +2 7 F2 F2 06 +3 0 DIP 6 +3 1 5 (%) 5 2E +3 2 T T 2C +3 3 R R 2D +3 4 F F 2B +3 5 G G 34 +3 6 V V 2A +3 7 F3 F3 04 +4 0 DIP 5 +4 1 F4 F4 0C +4 2 7 (') 7 3D +4 3 6 (&) 6 36 +4 4 Y Y 35 +4 5 H H 33 +4 6 B B 32 +4 7 F5 F5 03 +5 0 DIP 4 +5 1 8 (() 8 3E +5 2 I I 43 +5 3 U U 3C +5 4 J J 3B +5 5 N N 31 +5 6 M M 3A +5 7 F6 F6 0B +6 0 DIP 3 +6 1 F7 F7 83 +6 2 9 ()) 9 46 +6 3 O O 44 +6 4 K K 42 +6 5 L L 4B +6 6 , (<) , 41 +6 7 F8 F8 0A +7 0 DIP 2 +7 1 - (=) - 4E +7 2 0 0 45 +7 3 P P 4D +7 4 @ ` 0E +7 5 ; (+) ; 4C +7 6 . (>) . 49 +7 7 F9 F9 01 +8 0 DIP 1 +8 1 ^ (~) = 55 +8 2 _ (£) # 5D +8 3 [ ({) [ 54 +8 4 : (*) ' 52 +8 5 ] (}) ] 5B +8 6 / (?) / 4A +8 7 \ (|) \ 61 +9 0 DIP 0 +9 1 LEFT LEFT E0,6B +9 2 DOWN DOWN E0,72 +9 3 UP UP E0,75 +9 4 RETURN RETURN 5A +9 5 DELETE BACKSPACE 66 +9 6 COPY END E0,69 +9 7 RIGHT RIGHT E0,74 diff --git a/quartus/keyboard.vhd b/quartus/keyboard.vhd new file mode 100644 index 0000000..c0b8603 --- /dev/null +++ b/quartus/keyboard.vhd @@ -0,0 +1,294 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- BBC keyboard implementation with interface to PS/2 +-- +-- (C) 2011 Mike Stirling +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity keyboard is +port ( + CLOCK : in std_logic; + nRESET : in std_logic; + CLKEN_1MHZ : in std_logic; + + -- PS/2 interface + PS2_CLK : in std_logic; + PS2_DATA : in std_logic; + + -- If 1 then column is incremented automatically at + -- 1 MHz rate + AUTOSCAN : in std_logic; + + COLUMN : in std_logic_vector(3 downto 0); + ROW : in std_logic_vector(2 downto 0); + + -- 1 when currently selected key is down (AUTOSCAN disabled) + KEYPRESS : out std_logic; + -- 1 when any key is down (except row 0) + INT : out std_logic; + -- BREAK key output - 1 when pressed + BREAK_OUT : out std_logic; + + -- DIP switch inputs + DIP_SWITCH : in std_logic_vector(7 downto 0) + ); +end entity; + +architecture rtl of keyboard is + +-- PS/2 interface +component ps2_intf is +generic (filter_length : positive := 8); +port( + CLK : in std_logic; + nRESET : in std_logic; + + -- PS/2 interface (could be bi-dir) + PS2_CLK : in std_logic; + PS2_DATA : in std_logic; + + -- Byte-wide data interface - only valid for one clock + -- so must be latched externally if required + DATA : out std_logic_vector(7 downto 0); + VALID : out std_logic; + ERROR : out std_logic + ); +end component; + +-- Interface to PS/2 block +signal keyb_data : std_logic_vector(7 downto 0); +signal keyb_valid : std_logic; +signal keyb_error : std_logic; + +-- Internal signals +type key_matrix is array(0 to 15) of std_logic_vector(7 downto 0); +signal keys : key_matrix; +signal col : unsigned(3 downto 0); +signal release : std_logic; +signal extended : std_logic; +begin + + ps2 : ps2_intf port map ( + CLOCK, nRESET, + PS2_CLK, PS2_DATA, + keyb_data, keyb_valid, keyb_error + ); + + -- Column counts automatically when AUTOSCAN is enabled, otherwise + -- value is loaded from external input + process(CLOCK,nRESET) + begin + if nRESET = '0' then + col <= (others => '0'); + elsif rising_edge(CLOCK) then + if AUTOSCAN = '0' then + -- If autoscan disabled then transfer current COLUMN to counter + -- immediately (don't wait for next 1 MHz cycle) + col <= unsigned(COLUMN); + elsif CLKEN_1MHZ = '1' then + -- Otherwise increment the counter once per 1 MHz tick + col <= col + 1; + end if; + end if; + end process; + + -- Generate interrupt if any key in currently scanned column is pressed + -- (apart from in row 0). Output selected key status if autoscan disabled. + process(keys,col,ROW,AUTOSCAN) + variable k : std_logic_vector(7 downto 0); + begin + -- Shortcut to current key column + k := keys(to_integer(col)); + + -- Interrupt if any key pressed in rows 1 to 7. + INT <= k(7) or k(6) or k(5) or k(4) or k(3) or k(2) or k(1); + + -- Determine which key is pressed + -- Inhibit output during auto-scan + if AUTOSCAN = '0' then + KEYPRESS <= k(to_integer(unsigned(ROW))); + else + KEYPRESS <= '0'; + end if; + end process; + + -- Decode PS/2 data + process(CLOCK,nRESET) + begin + if nRESET = '0' then + release <= '0'; + extended <= '0'; + + BREAK_OUT <= '0'; + + keys(0) <= (others => '0'); + keys(1) <= (others => '0'); + keys(2) <= (others => '0'); + keys(3) <= (others => '0'); + keys(4) <= (others => '0'); + keys(5) <= (others => '0'); + keys(6) <= (others => '0'); + keys(7) <= (others => '0'); + keys(8) <= (others => '0'); + keys(9) <= (others => '0'); + -- These non-existent rows are used in the BBC master + keys(10) <= (others => '0'); + keys(11) <= (others => '0'); + keys(12) <= (others => '0'); + keys(13) <= (others => '0'); + keys(14) <= (others => '0'); + keys(15) <= (others => '0'); + elsif rising_edge(CLOCK) then + -- Copy DIP switches through to row 0 + keys(2)(0) <= DIP_SWITCH(7); + keys(3)(0) <= DIP_SWITCH(6); + keys(4)(0) <= DIP_SWITCH(5); + keys(5)(0) <= DIP_SWITCH(4); + keys(6)(0) <= DIP_SWITCH(3); + keys(7)(0) <= DIP_SWITCH(2); + keys(8)(0) <= DIP_SWITCH(1); + keys(9)(0) <= DIP_SWITCH(0); + + if keyb_valid = '1' then + -- Decode keyboard input + if keyb_data = X"e0" then + -- Extended key code follows + extended <= '1'; + elsif keyb_data = X"f0" then + -- Release code follows + release <= '1'; + else + -- Cancel extended/release flags for next time + release <= '0'; + extended <= '0'; + + -- Decode scan codes + case keyb_data is + when X"12" => keys(0)(0) <= not release; -- Left SHIFT + when X"59" => keys(0)(0) <= not release; -- Right SHIFT + when X"15" => keys(0)(1) <= not release; -- Q + when X"09" => keys(0)(2) <= not release; -- F10 (F0) + when X"16" => keys(0)(3) <= not release; -- 1 + when X"58" => keys(0)(4) <= not release; -- CAPS LOCK + when X"11" => keys(0)(5) <= not release; -- LEFT ALT (SHIFT LOCK) + when X"0D" => keys(0)(6) <= not release; -- TAB + when X"76" => keys(0)(7) <= not release; -- ESCAPE + when X"14" => keys(1)(0) <= not release; -- LEFT/RIGHT CTRL (CTRL) + when X"26" => keys(1)(1) <= not release; -- 3 + when X"1D" => keys(1)(2) <= not release; -- W + when X"1E" => keys(1)(3) <= not release; -- 2 + when X"1C" => keys(1)(4) <= not release; -- A + when X"1B" => keys(1)(5) <= not release; -- S + when X"1A" => keys(1)(6) <= not release; -- Z + when X"05" => keys(1)(7) <= not release; -- F1 + when X"25" => keys(2)(1) <= not release; -- 4 + when X"24" => keys(2)(2) <= not release; -- E + when X"23" => keys(2)(3) <= not release; -- D + when X"22" => keys(2)(4) <= not release; -- X + when X"21" => keys(2)(5) <= not release; -- C + when X"29" => keys(2)(6) <= not release; -- SPACE + when X"06" => keys(2)(7) <= not release; -- F2 + when X"2E" => keys(3)(1) <= not release; -- 5 + when X"2C" => keys(3)(2) <= not release; -- T + when X"2D" => keys(3)(3) <= not release; -- R + when X"2B" => keys(3)(4) <= not release; -- F + when X"34" => keys(3)(5) <= not release; -- G + when X"2A" => keys(3)(6) <= not release; -- V + when X"04" => keys(3)(7) <= not release; -- F3 + when X"0C" => keys(4)(1) <= not release; -- F4 + when X"3D" => keys(4)(2) <= not release; -- 7 + when X"36" => keys(4)(3) <= not release; -- 6 + when X"35" => keys(4)(4) <= not release; -- Y + when X"33" => keys(4)(5) <= not release; -- H + when X"32" => keys(4)(6) <= not release; -- B + when X"03" => keys(4)(7) <= not release; -- F5 + when X"3E" => keys(5)(1) <= not release; -- 8 + when X"43" => keys(5)(2) <= not release; -- I + when X"3C" => keys(5)(3) <= not release; -- U + when X"3B" => keys(5)(4) <= not release; -- J + when X"31" => keys(5)(5) <= not release; -- N + when X"3A" => keys(5)(6) <= not release; -- M + when X"0B" => keys(5)(7) <= not release; -- F6 + when X"83" => keys(6)(1) <= not release; -- F7 + when X"46" => keys(6)(2) <= not release; -- 9 + when X"44" => keys(6)(3) <= not release; -- O + when X"42" => keys(6)(4) <= not release; -- K + when X"4B" => keys(6)(5) <= not release; -- L + when X"41" => keys(6)(6) <= not release; -- , + when X"0A" => keys(6)(7) <= not release; -- F8 + when X"4E" => keys(7)(1) <= not release; -- - + when X"45" => keys(7)(2) <= not release; -- 0 + when X"4D" => keys(7)(3) <= not release; -- P + when X"0E" => keys(7)(4) <= not release; -- ` (@) + when X"4C" => keys(7)(5) <= not release; -- ; + when X"49" => keys(7)(6) <= not release; -- . + when X"01" => keys(7)(7) <= not release; -- F9 + when X"55" => keys(8)(1) <= not release; -- = (^) + when X"5D" => keys(8)(2) <= not release; -- # (_) + when X"54" => keys(8)(3) <= not release; -- [ + when X"52" => keys(8)(4) <= not release; -- ' + when X"5B" => keys(8)(5) <= not release; -- ] + when X"4A" => keys(8)(6) <= not release; -- / + when X"61" => keys(8)(7) <= not release; -- \ + when X"6B" => keys(9)(1) <= not release; -- LEFT + when X"72" => keys(9)(2) <= not release; -- DOWN + when X"75" => keys(9)(3) <= not release; -- UP + when X"5A" => keys(9)(4) <= not release; -- RETURN + when X"66" => keys(9)(5) <= not release; -- BACKSPACE (DELETE) + when X"69" => keys(9)(6) <= not release; -- END (COPY) + when X"74" => keys(9)(7) <= not release; -- RIGHT + + -- F12 is used for the BREAK key, which in the real BBC asserts + -- reset. Here we pass this out to the top level which may + -- optionally OR it in to the system reset + when X"07" => BREAK_OUT <= not release; -- F12 (BREAK) + + when others => null; + end case; + + end if; + end if; + end if; + end process; + +end architecture; + + diff --git a/quartus/m6522.vhd b/quartus/m6522.vhd new file mode 100644 index 0000000..e01ed10 --- /dev/null +++ b/quartus/m6522.vhd @@ -0,0 +1,886 @@ +-- +-- A simulation model of VIC20 hardware +-- Copyright (c) MikeJ - March 2003 +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS CODE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- You are responsible for any legal issues arising from your use of this code. +-- +-- The latest version of this file can be found at: www.fpgaarcade.com +-- +-- Email vic20@fpgaarcade.com +-- +-- +-- Revision list +-- +-- version 002 fix from Mark McDougall, untested +-- version 001 initial release +-- not very sure about the shift register, documentation is a bit light. + +library ieee ; + use ieee.std_logic_1164.all ; + use ieee.std_logic_unsigned.all; + use ieee.numeric_std.all; + +--library UNISIM; +-- use UNISIM.Vcomponents.all; + +entity M6522 is + port ( + + I_RS : in std_logic_vector(3 downto 0); + I_DATA : in std_logic_vector(7 downto 0); + O_DATA : out std_logic_vector(7 downto 0); + O_DATA_OE_L : out std_logic; + + I_RW_L : in std_logic; + I_CS1 : in std_logic; + I_CS2_L : in std_logic; + + O_IRQ_L : out std_logic; -- note, not open drain + -- port a + I_CA1 : in std_logic; + I_CA2 : in std_logic; + O_CA2 : out std_logic; + O_CA2_OE_L : out std_logic; + + I_PA : in std_logic_vector(7 downto 0); + O_PA : out std_logic_vector(7 downto 0); + O_PA_OE_L : out std_logic_vector(7 downto 0); + + -- port b + I_CB1 : in std_logic; + O_CB1 : out std_logic; + O_CB1_OE_L : out std_logic; + + I_CB2 : in std_logic; + O_CB2 : out std_logic; + O_CB2_OE_L : out std_logic; + + I_PB : in std_logic_vector(7 downto 0); + O_PB : out std_logic_vector(7 downto 0); + O_PB_OE_L : out std_logic_vector(7 downto 0); + + I_P2_H : in std_logic; -- high for phase 2 clock ____----__ + RESET_L : in std_logic; + ENA_4 : in std_logic; -- clk enable + CLK : in std_logic + ); +end; + +architecture RTL of M6522 is + + signal phase : std_logic_vector(1 downto 0); + signal p2_h_t1 : std_logic; + signal cs : std_logic; + + -- registers + signal r_ddra : std_logic_vector(7 downto 0); + signal r_ora : std_logic_vector(7 downto 0); + signal r_ira : std_logic_vector(7 downto 0); + + signal r_ddrb : std_logic_vector(7 downto 0); + signal r_orb : std_logic_vector(7 downto 0); + signal r_irb : std_logic_vector(7 downto 0); + + signal r_t1l_l : std_logic_vector(7 downto 0); + signal r_t1l_h : std_logic_vector(7 downto 0); + signal r_t2l_l : std_logic_vector(7 downto 0); + signal r_t2l_h : std_logic_vector(7 downto 0); -- not in real chip + signal r_sr : std_logic_vector(7 downto 0); + signal r_acr : std_logic_vector(7 downto 0); + signal r_pcr : std_logic_vector(7 downto 0); + signal r_ifr : std_logic_vector(7 downto 0); + signal r_ier : std_logic_vector(6 downto 0); + + signal sr_write_ena : boolean; + signal sr_read_ena : boolean; + signal ifr_write_ena : boolean; + signal ier_write_ena : boolean; + signal clear_irq : std_logic_vector(7 downto 0); + signal load_data : std_logic_vector(7 downto 0); + + -- timer 1 + signal t1c : std_logic_vector(15 downto 0); + signal t1c_active : boolean; + signal t1c_done : boolean; + signal t1_w_reset_int : boolean; + signal t1_r_reset_int : boolean; + signal t1_load_counter : boolean; + signal t1_reload_counter : boolean; + signal t1_toggle : std_logic; + signal t1_irq : std_logic := '0'; + + -- timer 2 + signal t2c : std_logic_vector(15 downto 0); + signal t2c_active : boolean; + signal t2c_done : boolean; + signal t2_pb6 : std_logic; + signal t2_pb6_t1 : std_logic; + signal t2_w_reset_int : boolean; + signal t2_r_reset_int : boolean; + signal t2_load_counter : boolean; + signal t2_reload_counter : boolean; + signal t2_irq : std_logic := '0'; + signal t2_sr_ena : boolean; + + -- shift reg + signal sr_cnt : std_logic_vector(3 downto 0); + signal sr_cb1_oe_l : std_logic; + signal sr_cb1_out : std_logic; + signal sr_drive_cb2 : std_logic; + signal sr_strobe : std_logic; + signal sr_strobe_t1 : std_logic; + signal sr_strobe_falling : boolean; + signal sr_strobe_rising : boolean; + signal sr_irq : std_logic; + signal sr_out : std_logic; + signal sr_off_delay : std_logic; + + -- io + signal w_orb_hs : std_logic; + signal w_ora_hs : std_logic; + signal r_irb_hs : std_logic; + signal r_ira_hs : std_logic; + + signal ca_hs_sr : std_logic; + signal ca_hs_pulse : std_logic; + signal cb_hs_sr : std_logic; + signal cb_hs_pulse : std_logic; + + signal cb1_in_mux : std_logic; + signal ca1_ip_reg : std_logic; + signal cb1_ip_reg : std_logic; + signal ca1_int : boolean; + signal cb1_int : boolean; + signal ca1_irq : std_logic; + signal cb1_irq : std_logic; + + signal ca2_ip_reg : std_logic; + signal cb2_ip_reg : std_logic; + signal ca2_int : boolean; + signal cb2_int : boolean; + signal ca2_irq : std_logic; + signal cb2_irq : std_logic; + + signal final_irq : std_logic; +begin + p_phase : process + begin + -- internal clock phase + wait until rising_edge(CLK); + if (ENA_4 = '1') then + p2_h_t1 <= I_P2_H; + if (p2_h_t1 = '0') and (I_P2_H = '1') then + phase <= "11"; + else + phase <= phase + "1"; + end if; + end if; + end process; + + p_cs : process(I_CS1, I_CS2_L, I_P2_H) + begin + cs <= '0'; + if (I_CS1 = '1') and (I_CS2_L = '0') and (I_P2_H = '1') then + cs <= '1'; + end if; + end process; + + -- peripheral control reg (pcr) + -- 0 ca1 interrupt control (0 +ve edge, 1 -ve edge) + -- 3..1 ca2 operation + -- 000 input -ve edge + -- 001 independend interrupt input -ve edge + -- 010 input +ve edge + -- 011 independend interrupt input +ve edge + -- 100 handshake output + -- 101 pulse output + -- 110 low output + -- 111 high output + -- 7..4 as 3..0 for cb1,cb2 + + -- auxiliary control reg (acr) + -- 0 input latch PA (0 disable, 1 enable) + -- 1 input latch PB (0 disable, 1 enable) + -- 4..2 shift reg control + -- 000 disable + -- 001 shift in using t2 + -- 010 shift in using o2 + -- 011 shift in using ext clk + -- 100 shift out free running t2 rate + -- 101 shift out using t2 + -- 101 shift out using o2 + -- 101 shift out using ext clk + -- 5 t2 timer control (0 timed interrupt, 1 count down with pulses on pb6) + -- 7..6 t1 timer control + -- 00 timed interrupt each time t1 is loaded pb7 disable + -- 01 continuous interrupts pb7 disable + -- 00 timed interrupt each time t1 is loaded pb7 one shot output + -- 01 continuous interrupts pb7 square wave output + -- + + p_write_reg_reset : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + r_ora <= x"00"; r_orb <= x"00"; + r_ddra <= x"00"; r_ddrb <= x"00"; + r_acr <= x"00"; r_pcr <= x"00"; + + w_orb_hs <= '0'; + w_ora_hs <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + w_orb_hs <= '0'; + w_ora_hs <= '0'; + if (cs = '1') and (I_RW_L = '0') then + case I_RS is + when x"0" => r_orb <= I_DATA; w_orb_hs <= '1'; + when x"1" => r_ora <= I_DATA; w_ora_hs <= '1'; + when x"2" => r_ddrb <= I_DATA; + when x"3" => r_ddra <= I_DATA; + + when x"B" => r_acr <= I_DATA; + when x"C" => r_pcr <= I_DATA; + when x"F" => r_ora <= I_DATA; + + when others => null; + end case; + end if; + + if (r_acr(7) = '1') and (t1_toggle = '1') then + r_orb(7) <= not r_orb(7); -- toggle + end if; + end if; + end if; + end process; + + p_write_reg : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + t1_w_reset_int <= false; + t1_load_counter <= false; + + t2_w_reset_int <= false; + t2_load_counter <= false; + + load_data <= x"00"; + sr_write_ena <= false; + ifr_write_ena <= false; + ier_write_ena <= false; + + if (cs = '1') and (I_RW_L = '0') then + load_data <= I_DATA; + case I_RS is + when x"4" => r_t1l_l <= I_DATA; + when x"5" => r_t1l_h <= I_DATA; t1_w_reset_int <= true; + t1_load_counter <= true; + + when x"6" => r_t1l_l <= I_DATA; + when x"7" => r_t1l_h <= I_DATA; t1_w_reset_int <= true; + + when x"8" => r_t2l_l <= I_DATA; + when x"9" => r_t2l_h <= I_DATA; t2_w_reset_int <= true; + t2_load_counter <= true; + + when x"A" => sr_write_ena <= true; + when x"D" => ifr_write_ena <= true; + when x"E" => ier_write_ena <= true; + + when others => null; + end case; + end if; + end if; + end process; + + p_oe : process(cs, I_RW_L) + begin + O_DATA_OE_L <= '1'; + if (cs = '1') and (I_RW_L = '1') then + O_DATA_OE_L <= '0'; + end if; + end process; + + p_read : process + begin + wait until rising_edge(CLK); + + if ENA_4 = '1' then + t1_r_reset_int <= false; + t2_r_reset_int <= false; + sr_read_ena <= false; + r_irb_hs <= '0'; + r_ira_hs <= '0'; + + if (cs = '1') and (I_RW_L = '1') then + case I_RS is + --when x"0" => O_DATA <= r_irb; r_irb_hs <= '1'; + -- fix from Mark McDougall, untested + when x"0" => O_DATA <= (r_irb and not r_ddrb) or (r_orb and r_ddrb); r_irb_hs <= '1'; + when x"1" => O_DATA <= r_ira; r_ira_hs <= '1'; + when x"2" => O_DATA <= r_ddrb; + when x"3" => O_DATA <= r_ddra; + when x"4" => O_DATA <= t1c( 7 downto 0); t1_r_reset_int <= true; + when x"5" => O_DATA <= t1c(15 downto 8); + when x"6" => O_DATA <= r_t1l_l; + when x"7" => O_DATA <= r_t1l_h; + when x"8" => O_DATA <= t2c( 7 downto 0); t2_r_reset_int <= true; + when x"9" => O_DATA <= t2c(15 downto 8); + when x"A" => O_DATA <= r_sr; sr_read_ena <= true; + when x"B" => O_DATA <= r_acr; + when x"C" => O_DATA <= r_pcr; + when x"D" => O_DATA <= r_ifr; + when x"E" => O_DATA <= ('0' & r_ier); + when x"F" => O_DATA <= r_ira; + when others => null; + end case; + end if; + end if; + + end process; + -- + -- IO + -- + p_ca1_cb1_sel : process(sr_cb1_oe_l, sr_cb1_out, I_CB1) + begin + -- if the shift register is enabled, cb1 may be an output + -- in this case, we should listen to the CB1_OUT for the interrupt + if (sr_cb1_oe_l = '1') then + cb1_in_mux <= I_CB1; + else + cb1_in_mux <= sr_cb1_out; + end if; + end process; + + p_ca1_cb1_int : process(r_pcr, ca1_ip_reg, I_CA1, cb1_ip_reg, cb1_in_mux) + begin + if (r_pcr(0) = '0') then -- ca1 control + -- negative edge + ca1_int <= (ca1_ip_reg = '1') and (I_CA1 = '0'); + else + -- positive edge + ca1_int <= (ca1_ip_reg = '0') and (I_CA1 = '1'); + end if; + + if (r_pcr(4) = '0') then -- cb1 control + -- negative edge + cb1_int <= (cb1_ip_reg = '1') and (cb1_in_mux = '0'); + else + -- positive edge + cb1_int <= (cb1_ip_reg = '0') and (cb1_in_mux = '1'); + end if; + end process; + + p_ca2_cb2_int : process(r_pcr, ca2_ip_reg, I_CA2, cb2_ip_reg, I_CB2) + begin + ca2_int <= false; + if (r_pcr(3) = '0') then -- ca2 input + if (r_pcr(2) = '0') then -- ca2 edge + -- negative edge + ca2_int <= (ca2_ip_reg = '1') and (I_CA2 = '0'); + else + -- positive edge + ca2_int <= (ca2_ip_reg = '0') and (I_CA2 = '1'); + end if; + end if; + + cb2_int <= false; + if (r_pcr(7) = '0') then -- cb2 input + if (r_pcr(6) = '0') then -- cb2 edge + -- negative edge + cb2_int <= (cb2_ip_reg = '1') and (I_CB2 = '0'); + else + -- positive edge + cb2_int <= (cb2_ip_reg = '0') and (I_CB2 = '1'); + end if; + end if; + end process; + + p_ca2_cb2 : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + O_CA2 <= '0'; + O_CA2_OE_L <= '1'; + O_CB2 <= '0'; + O_CB2_OE_L <= '1'; + + ca_hs_sr <= '0'; + ca_hs_pulse <= '0'; + cb_hs_sr <= '0'; + cb_hs_pulse <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- ca + if (phase = "00") and ((w_ora_hs = '1') or (r_ira_hs = '1')) then + ca_hs_sr <= '1'; + elsif ca1_int then + ca_hs_sr <= '0'; + end if; + + if (phase = "00") then + ca_hs_pulse <= w_ora_hs or r_ira_hs; + end if; + + O_CA2_OE_L <= not r_pcr(3); -- ca2 output + case r_pcr(3 downto 1) is + when "000" => O_CA2 <= '0'; -- input + when "001" => O_CA2 <= '0'; -- input + when "010" => O_CA2 <= '0'; -- input + when "011" => O_CA2 <= '0'; -- input + when "100" => O_CA2 <= not (ca_hs_sr); -- handshake + when "101" => O_CA2 <= not (ca_hs_pulse); -- pulse + when "110" => O_CA2 <= '0'; -- low + when "111" => O_CA2 <= '1'; -- high + when others => null; + end case; + + -- cb + if (phase = "00") and (w_orb_hs = '1') then + cb_hs_sr <= '1'; + elsif cb1_int then + cb_hs_sr <= '0'; + end if; + + if (phase = "00") then + cb_hs_pulse <= w_orb_hs; + end if; + + O_CB2_OE_L <= not (r_pcr(7) or sr_drive_cb2); -- cb2 output or serial + if (sr_drive_cb2 = '1') then -- serial output + O_CB2 <= sr_out; + else + case r_pcr(7 downto 5) is + when "000" => O_CB2 <= '0'; -- input + when "001" => O_CB2 <= '0'; -- input + when "010" => O_CB2 <= '0'; -- input + when "011" => O_CB2 <= '0'; -- input + when "100" => O_CB2 <= not (cb_hs_sr); -- handshake + when "101" => O_CB2 <= not (cb_hs_pulse); -- pulse + when "110" => O_CB2 <= '0'; -- low + when "111" => O_CB2 <= '1'; -- high + when others => null; + end case; + end if; + end if; + end if; + end process; + O_CB1 <= sr_cb1_out; + O_CB1_OE_L <= sr_cb1_oe_l; + + p_ca_cb_irq : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + ca1_irq <= '0'; + ca2_irq <= '0'; + cb1_irq <= '0'; + cb2_irq <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- not pretty + if ca1_int then + ca1_irq <= '1'; + elsif (r_ira_hs = '1') or (w_ora_hs = '1') or (clear_irq(1) = '1') then + ca1_irq <= '0'; + end if; + + if ca2_int then + ca2_irq <= '1'; + else + if (((r_ira_hs = '1') or (w_ora_hs = '1')) and (r_pcr(1) = '0')) or + (clear_irq(0) = '1') then + ca2_irq <= '0'; + end if; + end if; + + if cb1_int then + cb1_irq <= '1'; + elsif (r_irb_hs = '1') or (w_orb_hs = '1') or (clear_irq(4) = '1') then + cb1_irq <= '0'; + end if; + + if cb2_int then + cb2_irq <= '1'; + else + if (((r_irb_hs = '1') or (w_orb_hs = '1')) and (r_pcr(5) = '0')) or + (clear_irq(3) = '1') then + cb2_irq <= '0'; + end if; + end if; + end if; + end if; + end process; + + p_input_reg : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + ca1_ip_reg <= '0'; + cb1_ip_reg <= '0'; + + ca2_ip_reg <= '0'; + cb2_ip_reg <= '0'; + + r_ira <= x"00"; + r_irb <= x"00"; + + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- we have a fast clock, so we can have input registers + ca1_ip_reg <= I_CA1; + cb1_ip_reg <= cb1_in_mux; + + ca2_ip_reg <= I_CA2; + cb2_ip_reg <= I_CB2; + + if (r_acr(0) = '0') then + r_ira <= I_PA; + else -- enable latching + if ca1_int then + r_ira <= I_PA; + end if; + end if; + + if (r_acr(1) = '0') then + r_irb <= I_PB; + else -- enable latching + if cb1_int then + r_irb <= I_PB; + end if; + end if; + end if; + end if; + end process; + + + p_buffers : process(r_ddra, r_ora, r_ddrb, r_acr, r_orb) + begin + -- data direction reg (ddr) 0 = input, 1 = output + O_PA <= r_ora; + O_PA_OE_L <= not r_ddra; + + if (r_acr(7) = '1') then -- not clear if r_ddrb(7) must be 1 as well + O_PB_OE_L(7) <= '0'; -- an output if under t1 control + else + O_PB_OE_L(7) <= not (r_ddrb(7)); + end if; + + O_PB_OE_L(6 downto 0) <= not r_ddrb(6 downto 0); + O_PB(7 downto 0) <= r_orb(7 downto 0); + + end process; + -- + -- Timer 1 + -- + p_timer1_done : process(t1c,phase,r_acr) + variable done : boolean; + begin + done := (t1c = x"0000"); + t1c_done <= done and (phase = "11"); + --if (phase = "11") then + t1_reload_counter <= done and (r_acr(6) = '1'); + --end if; + end process; + + p_timer1 : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if t1_load_counter or (t1_reload_counter and phase = "11") then + t1c( 7 downto 0) <= r_t1l_l; + t1c(15 downto 8) <= r_t1l_h; + elsif (phase="11") then + t1c <= t1c - "1"; + end if; + + if t1_load_counter or t1_reload_counter then + t1c_active <= true; + elsif t1c_done then + t1c_active <= false; + end if; + if RESET_L = '0' then + t1c_active <= false; + end if; + + t1_toggle <= '0'; + if t1c_active and t1c_done then + t1_toggle <= '1'; + t1_irq <= '1'; + elsif RESET_L = '0' or t1_w_reset_int or t1_r_reset_int or (clear_irq(6) = '1') then + t1_irq <= '0'; + end if; + end if; + end process; + -- + -- Timer2 + -- + p_timer2_pb6_input : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if (phase = "01") then -- leading edge p2_h + t2_pb6 <= I_PB(6); + t2_pb6_t1 <= t2_pb6; + end if; + end if; + end process; + + p_timer2_done : process(t2c,phase) + variable done : boolean; + begin + done := (t2c = x"0000"); + t2c_done <= done and (phase = "11"); + --if (phase = "11") then + t2_reload_counter <= done; + --end if; + end process; + + p_timer2 : process + variable ena : boolean; + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + if (r_acr(5) = '0') then + ena := true; + else + ena := (t2_pb6_t1 = '1') and (t2_pb6 = '0'); -- falling edge + end if; + + if t2_load_counter or (t2_reload_counter and phase = "11") then + -- not sure if t2c_reload should be here. Does timer2 just continue to + -- count down, or is it reloaded ? Reloaded makes more sense if using + -- it to generate a clock for the shift register. + t2c( 7 downto 0) <= r_t2l_l; + t2c(15 downto 8) <= r_t2l_h; + else + if (phase="11") and ena then -- or count mode + t2c <= t2c - "1"; + end if; + end if; + + t2_sr_ena <= (t2c(7 downto 0) = x"00") and (phase = "11"); + + if t2_load_counter then + t2c_active <= true; + elsif t2c_done then + t2c_active <= false; + end if; + if RESET_L = '0' then + t2c_active <= false; + end if; + + if t2c_active and t2c_done then + t2_irq <= '1'; + elsif RESET_L = '0' or t2_w_reset_int or t2_r_reset_int or (clear_irq(5) = '1') then + t2_irq <= '0'; + end if; + end if; + end process; + -- + -- Shift Register + -- + p_sr : process(RESET_L, CLK) + variable dir_out : std_logic; + variable ena : std_logic; + variable cb1_op : std_logic; + variable cb1_ip : std_logic; + variable use_t2 : std_logic; + variable free_run : std_logic; + variable sr_count_ena : boolean; + begin + if (RESET_L = '0') then + r_sr <= x"00"; + sr_drive_cb2 <= '0'; + sr_cb1_oe_l <= '1'; + sr_cb1_out <= '0'; + sr_strobe <= '1'; + sr_cnt <= "0000"; + sr_irq <= '0'; + sr_out <= '1'; + sr_off_delay <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + -- decode mode + dir_out := r_acr(4); -- output on cb2 + cb1_op := '0'; + cb1_ip := '0'; + use_t2 := '0'; + free_run := '0'; + + case r_acr(4 downto 2) is + when "000" => ena := '0'; cb1_ip := '1'; + when "001" => ena := '1'; cb1_op := '1'; use_t2 := '1'; + when "010" => ena := '1'; cb1_op := '1'; + when "011" => ena := '1'; cb1_ip := '1'; + when "100" => ena := '1'; use_t2 := '1'; free_run := '1'; + when "101" => ena := '1'; cb1_op := '1'; use_t2 := '1'; + when "110" => ena := '1'; + when "111" => ena := '1'; cb1_ip := '1'; + when others => null; + end case; + + -- clock select + -- SR still runs even in disabled mode (on rising edge of CB1). It + -- just doesn't generate any interrupts. + -- Ref BBC micro advanced user guide p409 + if (cb1_ip = '1') then + sr_strobe <= I_CB1; + else + if (sr_cnt(3) = '0') and (free_run = '0') then + sr_strobe <= '1'; + else + if ((use_t2 = '1') and t2_sr_ena) or + ((use_t2 = '0') and (phase = "00")) then + sr_strobe <= not sr_strobe; + end if; + end if; + end if; + + -- latch on rising edge, shift on falling edge + if sr_write_ena then + r_sr <= load_data; + else + if (dir_out = '0') then + -- input + if (sr_cnt(3) = '1') or (cb1_ip = '1') then + if sr_strobe_rising then + r_sr <= r_sr(6 downto 0) & I_CB2; + end if; + end if; + sr_out <= '1'; + else + -- output + if (sr_cnt(3) = '1') or (sr_off_delay = '1') or (cb1_ip = '1') or (free_run = '1') then + if sr_strobe_falling then + r_sr(7 downto 1) <= r_sr(6 downto 0); + r_sr(0) <= r_sr(7); + sr_out <= r_sr(7); + end if; + else + sr_out <= '1'; + end if; + end if; + end if; + + sr_count_ena := sr_strobe_rising; + + if sr_write_ena or sr_read_ena then + -- some documentation says sr bit in IFR must be set as well ? + sr_cnt <= "1000"; + elsif sr_count_ena and (sr_cnt(3) = '1') then + sr_cnt <= sr_cnt + "1"; + end if; + + if (phase = "00") then + sr_off_delay <= sr_cnt(3); -- give some hold time when shifting out + end if; + + if sr_count_ena and (sr_cnt = "1111") and (ena = '1') and (free_run = '0') then + sr_irq <= '1'; + elsif sr_write_ena or sr_read_ena or (clear_irq(2) = '1') then + sr_irq <= '0'; + end if; + + -- assign ops + sr_drive_cb2 <= dir_out; + sr_cb1_oe_l <= not cb1_op; + sr_cb1_out <= sr_strobe; + end if; + end if; + end process; + + p_sr_strobe_rise_fall : process + begin + wait until rising_edge(CLK); + if (ENA_4 = '1') then + sr_strobe_t1 <= sr_strobe; + sr_strobe_rising <= (sr_strobe_t1 = '0') and (sr_strobe = '1'); + sr_strobe_falling <= (sr_strobe_t1 = '1') and (sr_strobe = '0'); + end if; + end process; + -- + -- Interrupts + -- + p_ier : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + r_ier <= "0000000"; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + if ier_write_ena then + if (load_data(7) = '1') then + -- set + r_ier <= r_ier or load_data(6 downto 0); + else + -- clear + r_ier <= r_ier and not load_data(6 downto 0); + end if; + end if; + end if; + end if; + end process; + + p_ifr : process(t1_irq, t2_irq, final_irq, ca1_irq, ca2_irq, sr_irq, + cb1_irq, cb2_irq) + begin + r_ifr(7) <= final_irq; + r_ifr(6) <= t1_irq; + r_ifr(5) <= t2_irq; + r_ifr(4) <= cb1_irq; + r_ifr(3) <= cb2_irq; + r_ifr(2) <= sr_irq; + r_ifr(1) <= ca1_irq; + r_ifr(0) <= ca2_irq; + + O_IRQ_L <= not final_irq; + end process; + + p_irq : process(RESET_L, CLK) + begin + if (RESET_L = '0') then + final_irq <= '0'; + elsif rising_edge(CLK) then + if (ENA_4 = '1') then + if ((r_ifr(6 downto 0) and r_ier(6 downto 0)) = "0000000") then + final_irq <= '0'; -- no interrupts + else + final_irq <= '1'; + end if; + end if; + end if; + end process; + + p_clear_irq : process(ifr_write_ena, load_data) + begin + clear_irq <= x"00"; + if ifr_write_ena then + clear_irq <= load_data; + end if; + end process; + +end architecture RTL; diff --git a/quartus/m6522_tb.vhd b/quartus/m6522_tb.vhd new file mode 100644 index 0000000..3e4262d --- /dev/null +++ b/quartus/m6522_tb.vhd @@ -0,0 +1,378 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity m6522_tb is +end entity; + +architecture tb of m6522_tb is + +component M6522 is + port ( + + I_RS : in std_logic_vector(3 downto 0); + I_DATA : in std_logic_vector(7 downto 0); + O_DATA : out std_logic_vector(7 downto 0); + O_DATA_OE_L : out std_logic; + + I_RW_L : in std_logic; + I_CS1 : in std_logic; + I_CS2_L : in std_logic; + + O_IRQ_L : out std_logic; -- note, not open drain + -- port a + I_CA1 : in std_logic; + I_CA2 : in std_logic; + O_CA2 : out std_logic; + O_CA2_OE_L : out std_logic; + + I_PA : in std_logic_vector(7 downto 0); + O_PA : out std_logic_vector(7 downto 0); + O_PA_OE_L : out std_logic_vector(7 downto 0); + + -- port b + I_CB1 : in std_logic; + O_CB1 : out std_logic; + O_CB1_OE_L : out std_logic; + + I_CB2 : in std_logic; + O_CB2 : out std_logic; + O_CB2_OE_L : out std_logic; + + I_PB : in std_logic_vector(7 downto 0); + O_PB : out std_logic_vector(7 downto 0); + O_PB_OE_L : out std_logic_vector(7 downto 0); + + I_P2_H : in std_logic; -- high for phase 2 clock ____----__ + RESET_L : in std_logic; + ENA_4 : in std_logic; -- clk enable + CLK : in std_logic + ); +end component; + +signal rs : std_logic_vector(3 downto 0) := "0000"; +signal di : std_logic_vector(7 downto 0) := "00000000"; +signal do : std_logic_vector(7 downto 0); +signal n_d_oe : std_logic; +signal r_nw : std_logic := '1'; +signal cs1 : std_logic := '0'; +signal n_cs2 : std_logic := '0'; +signal n_irq : std_logic; +signal ca1_in : std_logic := '0'; +signal ca2_in : std_logic := '0'; +signal ca2_out : std_logic; +signal n_ca2_oe : std_logic; +signal pa_in : std_logic_vector(7 downto 0) := "00000000"; +signal pa_out : std_logic_vector(7 downto 0); +signal n_pa_oe : std_logic_vector(7 downto 0); +signal cb1_in : std_logic := '0'; +signal cb1_out : std_logic; +signal n_cb1_oe : std_logic; +signal cb2_in : std_logic := '0'; +signal cb2_out : std_logic; +signal n_cb2_oe : std_logic; +signal pb_in : std_logic_vector(7 downto 0) := "00000000"; +signal pb_out : std_logic_vector(7 downto 0); +signal n_pb_oe : std_logic_vector(7 downto 0); + +signal phase2 : std_logic := '0'; +signal n_reset : std_logic := '0'; +signal clken : std_logic := '0'; +signal clock : std_logic := '0'; + +begin + + uut: m6522 port map ( + rs, di, do, n_d_oe, + r_nw, cs1, n_cs2, n_irq, + ca1_in, ca2_in, ca2_out, n_ca2_oe, + pa_in, pa_out, n_pa_oe, + cb1_in, cb1_out, n_cb1_oe, + cb2_in, cb2_out, n_cb2_oe, + pb_in, pb_out, n_pb_oe, + phase2, n_reset, clken, clock + ); + + clock <= not clock after 125 ns; -- 4x 1 MHz + phase2 <= not phase2 after 500 ns; + clken <= '1'; -- all cycles enabled + + process + begin + wait for 1 us; + -- Release reset + n_reset <= '1'; + end process; + + process + + procedure reg_write( + a : in std_logic_vector(3 downto 0); + d : in std_logic_vector(7 downto 0)) is + begin + wait until falling_edge(phase2); + rs <= a; + di <= d; + cs1 <= '1'; + r_nw <= '0'; + wait until falling_edge(phase2); + cs1 <= '0'; + r_nw <= '1'; + end procedure; + + procedure reg_read( + a : in std_logic_vector(3 downto 0)) is + begin + wait until falling_edge(phase2); + rs <= a; + cs1 <= '1'; + r_nw <= '1'; + wait until falling_edge(phase2); + cs1 <= '0'; + end procedure; + + begin + wait for 2 us; + + -- Set port A and B to output + reg_write("0010","11111111"); + reg_write("0011","11111111"); + + -- Write to port B + reg_write("0000","10101010"); + -- Write to port B + reg_write("0000","01010101"); + -- Write to port A (no handshake) + reg_write("1111","10101010"); + -- Write to port A (with handshake) + reg_write("0001","01010101"); + + -- Set port A and B to input + reg_write("0010","00000000"); + reg_write("0011","00000000"); + + -- Apply input stimuli and read from ports + pa_in <= "10101010"; + pb_in <= "01010101"; + reg_read("0000"); + reg_read("0001"); + + -- Test CA1 interrupt + ca1_in <= '0'; + reg_write("1100","00000001"); -- PCR - interrupt on rising edge + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1110","10000010"); -- Enable CA1 interrupt + ca1_in <= '1'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("0001"); -- Should clear interrupt + wait for 2 us; + reg_write("1100","00000000"); -- PCR - interrupt on falling edge + ca1_in <= '0'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_write("1101","00000010"); -- Should clear interrupt + wait for 2 us; + reg_write("1110","00000010"); -- Disable CA1 interrupt + + -- Test CB1 interrupt + cb1_in <= '0'; + reg_write("1100","00010000"); -- PCR - interrupt on rising edge + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1110","10010000"); -- Enable CB1 interrupt + cb1_in <= '1'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("0000"); -- Should clear interrupt + wait for 2 us; + reg_write("1100","00000000"); -- PCR - interrupt on falling edge + cb1_in <= '0'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_write("1101","00010000"); -- Should clear interrupt + wait for 2 us; + reg_write("1110","00010000"); -- Disable CA1 interrupt + + -- Test CA2 interrupt modes + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1110","10000001"); -- Enable CA2 interrupt + -- mode 2 (+ve edge, clear on read/write) + reg_write("1100","00000100"); -- PCR + ca2_in <= '1'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("1111"); -- Should not clear interrupt + reg_read("0001"); -- Should clear interrupt + wait for 2 us; + -- mode 0 (-ve edge, clear on read/write) + reg_write("1100","00000000"); -- PCR + ca2_in <= '0'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("1111"); -- Should not clear interrupt + reg_read("0001"); -- Should clear interrupt + wait for 2 us; + -- mode 3 (+ve edge, don't clear on read/write) + reg_write("1100","00000110"); + ca2_in <= '1'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("1111"); -- Should not clear interrupt + reg_read("0001"); -- Should not clear interrupt + reg_write("1101","00000001"); -- Should clear interrupt + wait for 2 us; + -- mode 1 (-ve edge, don't clear on read/write) + reg_write("1100","00000010"); + ca2_in <= '0'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("1111"); -- Should not clear interrupt + reg_read("0001"); -- Should not clear interrupt + reg_write("1101","00000001"); -- Should clear interrupt + wait for 2 us; + + -- Test CA2 output modes + -- mode 4 (set low on read/write of ORA, set high on CA1 interrupt edge) + reg_write("1100","00001000"); + -- mode 5 (set low for 1 cycle on read/write ORA) + reg_write("1100","00001010"); + -- mode 6 (held low) + reg_write("1100","00001100"); + -- mode 7 (held high) + reg_write("1100","00001110"); + + -- Test CB2 interrupt modes + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1110","10001000"); -- Enable CB2 interrupt + -- mode 2 (+ve edge, clear on read/write) + reg_write("1100","01000000"); -- PCR + cb2_in <= '1'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("0000"); -- Should clear interrupt + wait for 2 us; + -- mode 0 (-ve edge, clear on read/write) + reg_write("1100","00000000"); -- PCR + cb2_in <= '0'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("0000"); -- Should clear interrupt + wait for 2 us; + -- mode 3 (+ve edge, don't clear on read/write) + reg_write("1100","01100000"); + cb2_in <= '1'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("0000"); -- Should not clear interrupt + reg_write("1101","00001000"); -- Should clear interrupt + wait for 2 us; + -- mode 1 (-ve edge, don't clear on read/write) + reg_write("1100","00100000"); + cb2_in <= '0'; -- Trigger event + wait for 2 us; + reg_read("1101"); + reg_read("0000"); -- Should not clear interrupt + reg_write("1101","00001000"); -- Should clear interrupt + wait for 2 us; + + -- Test CB2 output modes + -- mode 4 (set low on read/write of ORA, set high on CA1 interrupt edge) + reg_write("1100","10000000"); + -- mode 5 (set low for 1 cycle on read/write ORA) + reg_write("1100","10100000"); + -- mode 6 (held low) + reg_write("1100","11000000"); + -- mode 7 (held high) + reg_write("1100","11100000"); + + -- Timer 1 timeout + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1110","11000000"); -- Enable timer 1 interrupt + -- Count to 16 + reg_write("0100","00010000"); + reg_write("0101","00000000"); + wait for 50 us; + -- Count to 16 + reg_write("0100","00010000"); + reg_write("0101","00000000"); -- Should clear interrupt + wait for 50 us; + reg_read("0100"); -- Should clear interrupt + + -- Timer 2 timeout + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1110","10100000"); -- Enable timer 2 interrupt + -- Count to 16 + reg_write("1000","00010000"); + reg_write("1001","00000000"); + wait for 50 us; + -- Count to 16 + reg_write("1000","00010000"); + reg_write("1001","00000000"); -- Should clear interrupt + wait for 50 us; + reg_read("1000"); -- Should clear interrupt + + -- Timer 2 test similar to BBC usage (speech interrupt) + -- PB6 high + pb_in(6) <= '1'; + reg_write("1101","01111111"); -- Clear interrupts + reg_write("1110","01111111"); -- Disable all interrupts + reg_write("1011","00100000"); -- Timer 2 PB6 counter mode + reg_write("1000","00000001"); -- Start at 1 + reg_write("1001","00000000"); + reg_write("1110","10100000"); -- Enable timer 2 interrupt + wait for 5 us; + -- Generate falling edge + pb_in(6) <= '0'; + wait for 5 us; + -- Clear interrupt + reg_write("1101","00100000"); + -- Zero timer high byte + reg_write("1001","00000000"); + + wait; + end process; + +end architecture; diff --git a/quartus/mc6845.vhd b/quartus/mc6845.vhd new file mode 100644 index 0000000..31208ea --- /dev/null +++ b/quartus/mc6845.vhd @@ -0,0 +1,481 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- MC6845 CRTC +-- +-- Synchronous implementation for FPGA +-- +-- (C) 2011 Mike Stirling +-- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity mc6845 is +port ( + CLOCK : in std_logic; + CLKEN : in std_logic; + nRESET : in std_logic; + + -- Bus interface + ENABLE : in std_logic; + R_nW : in std_logic; + RS : in std_logic; + DI : in std_logic_vector(7 downto 0); + DO : out std_logic_vector(7 downto 0); + + -- Display interface + VSYNC : out std_logic; + HSYNC : out std_logic; + DE : out std_logic; + CURSOR : out std_logic; + LPSTB : in std_logic; + + -- Memory interface + MA : out std_logic_vector(13 downto 0); + RA : out std_logic_vector(4 downto 0) + ); +end entity; + +architecture rtl of mc6845 is + +-- Host-accessible registers +signal addr_reg : std_logic_vector(4 downto 0); -- Currently addressed register +-- These are write-only +signal r00_h_total : unsigned(7 downto 0); -- Horizontal total, chars +signal r01_h_displayed : unsigned(7 downto 0); -- Horizontal active, chars +signal r02_h_sync_pos : unsigned(7 downto 0); -- Horizontal sync position, chars +signal r03_v_sync_width : unsigned(3 downto 0); -- Vertical sync width, scan lines (0=16 lines) +signal r03_h_sync_width : unsigned(3 downto 0); -- Horizontal sync width, chars (0=no sync) +signal r04_v_total : unsigned(6 downto 0); -- Vertical total, character rows +signal r05_v_total_adj : unsigned(4 downto 0); -- Vertical offset, scan lines +signal r06_v_displayed : unsigned(6 downto 0); -- Vertical active, character rows +signal r07_v_sync_pos : unsigned(6 downto 0); -- Vertical sync position, character rows +signal r08_interlace : std_logic_vector(1 downto 0); +signal r09_max_scan_line_addr : unsigned(4 downto 0); +signal r10_cursor_mode : std_logic_vector(1 downto 0); +signal r10_cursor_start : unsigned(4 downto 0); -- Cursor start, scan lines +signal r11_cursor_end : unsigned(4 downto 0); -- Cursor end, scan lines +signal r12_start_addr_h : unsigned(5 downto 0); +signal r13_start_addr_l : unsigned(7 downto 0); +-- These are read/write +signal r14_cursor_h : unsigned(5 downto 0); +signal r15_cursor_l : unsigned(7 downto 0); +-- These are read-only +signal r16_light_pen_h : unsigned(5 downto 0); +signal r17_light_pen_l : unsigned(7 downto 0); + + +-- Timing generation +-- Horizontal counter counts position on line +signal h_counter : unsigned(7 downto 0); +-- HSYNC counter counts duration of sync pulse +signal h_sync_counter : unsigned(3 downto 0); +-- Row counter counts current character row +signal row_counter : unsigned(6 downto 0); +-- Line counter counts current line within each character row +signal line_counter : unsigned(4 downto 0); +-- VSYNC counter counts duration of sync pulse +signal v_sync_counter : unsigned(3 downto 0); +-- Field counter counts number of complete fields for cursor flash +signal field_counter : unsigned(5 downto 0); + +-- Internal signals +signal h_sync_start : std_logic; +signal h_half_way : std_logic; +signal h_display : std_logic; +signal hs : std_logic; +signal v_display : std_logic; +signal vs : std_logic; +signal odd_field : std_logic; +signal ma_i : unsigned(13 downto 0); +signal ma_row_start : unsigned(13 downto 0); -- Start address of current character row +signal cursor_i : std_logic; +signal lpstb_i : std_logic; + + +begin + HSYNC <= hs; -- External HSYNC driven directly from internal signal + VSYNC <= vs; -- External VSYNC driven directly from internal signal + DE <= h_display and v_display; + + -- Cursor output generated combinatorially from the internal signal in + -- accordance with the currently selected cursor mode + CURSOR <= cursor_i when r10_cursor_mode = "00" else + '0' when r10_cursor_mode = "01" else + (cursor_i and field_counter(4)) when r10_cursor_mode = "10" else + (cursor_i and field_counter(5)); + + -- Synchronous register access. Enabled on every clock. + process(CLOCK,nRESET) + begin + if nRESET = '0' then + -- Reset registers to defaults + addr_reg <= (others => '0'); + r00_h_total <= (others => '0'); + r01_h_displayed <= (others => '0'); + r02_h_sync_pos <= (others => '0'); + r03_v_sync_width <= (others => '0'); + r03_h_sync_width <= (others => '0'); + r04_v_total <= (others => '0'); + r05_v_total_adj <= (others => '0'); + r06_v_displayed <= (others => '0'); + r07_v_sync_pos <= (others => '0'); + r08_interlace <= (others => '0'); + r09_max_scan_line_addr <= (others => '0'); + r10_cursor_mode <= (others => '0'); + r10_cursor_start <= (others => '0'); + r11_cursor_end <= (others => '0'); + r12_start_addr_h <= (others => '0'); + r13_start_addr_l <= (others => '0'); + r14_cursor_h <= (others => '0'); + r15_cursor_l <= (others => '0'); + + DO <= (others => '0'); + elsif rising_edge(CLOCK) then + if ENABLE = '1' then + if R_nW = '1' then + -- Read + case addr_reg is + when "01110" => + DO <= "00" & std_logic_vector(r14_cursor_h); + when "01111" => + DO <= std_logic_vector(r15_cursor_l); + when "10000" => + DO <= "00" & std_logic_vector(r16_light_pen_h); + when "10001" => + DO <= std_logic_vector(r17_light_pen_l); + when others => + DO <= (others => '0'); + end case; + else + -- Write + if RS = '0' then + addr_reg <= DI(4 downto 0); + else + case addr_reg is + when "00000" => + r00_h_total <= unsigned(DI); + when "00001" => + r01_h_displayed <= unsigned(DI); + when "00010" => + r02_h_sync_pos <= unsigned(DI); + when "00011" => + r03_v_sync_width <= unsigned(DI(7 downto 4)); + r03_h_sync_width <= unsigned(DI(3 downto 0)); + when "00100" => + r04_v_total <= unsigned(DI(6 downto 0)); + when "00101" => + r05_v_total_adj <= unsigned(DI(4 downto 0)); + when "00110" => + r06_v_displayed <= unsigned(DI(6 downto 0)); + when "00111" => + r07_v_sync_pos <= unsigned(DI(6 downto 0)); + when "01000" => + r08_interlace <= DI(1 downto 0); + when "01001" => + r09_max_scan_line_addr <= unsigned(DI(4 downto 0)); + when "01010" => + r10_cursor_mode <= DI(6 downto 5); + r10_cursor_start <= unsigned(DI(4 downto 0)); + when "01011" => + r11_cursor_end <= unsigned(DI(4 downto 0)); + when "01100" => + r12_start_addr_h <= unsigned(DI(5 downto 0)); + when "01101" => + r13_start_addr_l <= unsigned(DI(7 downto 0)); + when "01110" => + r14_cursor_h <= unsigned(DI(5 downto 0)); + when "01111" => + r15_cursor_l <= unsigned(DI(7 downto 0)); + when others => + null; + end case; + end if; + end if; + end if; + end if; + end process; -- registers + + -- Horizontal, vertical and address counters + process(CLOCK,nRESET) + variable ma_row_start : unsigned(13 downto 0); + variable max_scan_line : unsigned(4 downto 0); + begin + if nRESET = '0' then + -- H + h_counter <= (others => '0'); + + -- V + line_counter <= (others => '0'); + row_counter <= (others => '0'); + odd_field <= '0'; + + -- Fields (cursor flash) + field_counter <= (others => '0'); + + -- Addressing + ma_row_start := (others => '0'); + ma_i <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN='1' then + -- Horizontal counter increments on each clock, wrapping at + -- h_total + if h_counter = r00_h_total then + -- h_total reached + h_counter <= (others => '0'); + + -- In interlace sync + video mode mask off the LSb of the + -- max scan line address + if r08_interlace = "11" then + max_scan_line := r09_max_scan_line_addr(4 downto 1) & "0"; + else + max_scan_line := r09_max_scan_line_addr; + end if; + + -- Scan line counter increments, wrapping at max_scan_line_addr + if line_counter = max_scan_line then + -- Next character row + -- FIXME: No support for v_total_adj yet + line_counter <= (others => '0'); + if row_counter = r04_v_total then + -- If in interlace mode we toggle to the opposite field. + -- Save on some logic by doing this here rather than at the + -- end of v_total_adj - it shouldn't make any difference to the + -- output + if r08_interlace(0) = '1' then + odd_field <= not odd_field; + else + odd_field <= '0'; + end if; + + -- Address is loaded from start address register at the top of + -- each field and the row counter is reset + ma_row_start := r12_start_addr_h & r13_start_addr_l; + row_counter <= (others => '0'); + + -- Increment field counter + field_counter <= field_counter + 1; + else + -- On all other character rows within the field the row start address is + -- increased by h_displayed and the row counter is incremented + ma_row_start := ma_row_start + r01_h_displayed; + row_counter <= row_counter + 1; + end if; + else + -- Next scan line. Count in twos in interlaced sync+video mode + if r08_interlace = "11" then + line_counter <= line_counter + 2; + line_counter(0) <= '0'; -- Force to even + else + line_counter <= line_counter + 1; + end if; + end if; + + -- Memory address preset to row start at the beginning of each + -- scan line + ma_i <= ma_row_start; + else + -- Increment horizontal counter + h_counter <= h_counter + 1; + -- Increment memory address + ma_i <= ma_i + 1; + end if; + end if; + end process; + + -- Signals to mark hsync and half way points for generating + -- vsync in even and odd fields + process(h_counter) + begin + h_sync_start <= '0'; + h_half_way <= '0'; + + if h_counter = r02_h_sync_pos then + h_sync_start <= '1'; + end if; + if h_counter = "0" & r02_h_sync_pos(7 downto 1) then + h_half_way <= '1'; + end if; + end process; + + -- Video timing and sync counters + process(CLOCK,nRESET) + begin + if nRESET = '0' then + -- H + h_display <= '0'; + hs <= '0'; + h_sync_counter <= (others => '0'); + + -- V + v_display <= '0'; + vs <= '0'; + v_sync_counter <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Horizontal active video + if h_counter = 0 then + -- Start of active video + h_display <= '1'; + end if; + if h_counter = r01_h_displayed then + -- End of active video + h_display <= '0'; + end if; + + -- Horizontal sync + if h_sync_start = '1' or hs = '1' then + -- In horizontal sync + hs <= '1'; + h_sync_counter <= h_sync_counter + 1; + else + h_sync_counter <= (others => '0'); + end if; + if h_sync_counter = r03_h_sync_width then + -- Terminate hsync after h_sync_width (0 means no hsync so this + -- can immediately override the setting above) + hs <= '0'; + end if; + + -- Vertical active video + if row_counter = 0 then + -- Start of active video + v_display <= '1'; + end if; + if row_counter = r06_v_displayed then + -- End of active video + v_display <= '0'; + end if; + + -- Vertical sync occurs either at the same time as the horizontal sync (even fields) + -- or half a line later (odd fields) + if (odd_field = '0' and h_sync_start = '1') or (odd_field = '1' and h_half_way = '1') then + if (row_counter = r07_v_sync_pos and line_counter = 0) or vs = '1' then + -- In vertical sync + vs <= '1'; + v_sync_counter <= v_sync_counter + 1; + else + v_sync_counter <= (others => '0'); + end if; + if v_sync_counter = r03_v_sync_width and vs = '1' then + -- Terminate vsync after v_sync_width (0 means 16 lines so this is + -- masked by 'vs' to ensure a full turn of the counter in this case) + vs <= '0'; + end if; + end if; + end if; + end process; + + -- Address generation + process(CLOCK,nRESET) + variable slv_line : std_logic_vector(4 downto 0); + begin + if nRESET = '0' then + RA <= (others => '0'); + MA <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + slv_line := std_logic_vector(line_counter); + + -- Character row address is just the scan line counter delayed by + -- one clock to line up with the syncs. + if r08_interlace = "11" then + -- In interlace sync and video mode the LSb is determined by the + -- field number. The line counter counts up in 2s in this case. + RA <= slv_line(4 downto 1) & (slv_line(0) or odd_field); + else + RA <= slv_line; + end if; + -- Internal memory address delayed by one cycle as well + MA <= std_logic_vector(ma_i); + end if; + end process; + + -- Cursor control + process(CLOCK,nRESET) + variable cursor_line : std_logic; + begin + -- Internal cursor enable signal delayed by 1 clock to line up + -- with address outputs + if nRESET = '0' then + cursor_i <= '0'; + cursor_line := '0'; + elsif rising_edge(CLOCK) and CLKEN = '1' then + if h_display = '1' and v_display = '1' and ma_i = r14_cursor_h & r15_cursor_l then + if line_counter = 0 then + -- Suppress wrap around if last line is > max scan line + cursor_line := '0'; + end if; + if line_counter = r10_cursor_start then + -- First cursor scanline + cursor_line := '1'; + end if; + + -- Cursor output is asserted within the current cursor character + -- on the selected lines only + cursor_i <= cursor_line; + + if line_counter = r11_cursor_end then + -- Last cursor scanline + cursor_line := '0'; + end if; + else + -- Cursor is off in all character positions apart from the + -- selected one + cursor_i <= '0'; + end if; + end if; + end process; + + -- Light pen capture + process(CLOCK,nRESET) + begin + if nRESET = '0' then + lpstb_i <= '0'; + r16_light_pen_h <= (others => '0'); + r17_light_pen_l <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Register light-pen strobe input + lpstb_i <= LPSTB; + + if LPSTB = '1' and lpstb_i = '0' then + -- Capture address on rising edge + r16_light_pen_h <= ma_i(13 downto 8); + r17_light_pen_l <= ma_i(7 downto 0); + end if; + end if; + end process; +end architecture; + + diff --git a/quartus/pll32.ppf b/quartus/pll32.ppf new file mode 100644 index 0000000..1ba6b2c --- /dev/null +++ b/quartus/pll32.ppf @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/quartus/pll32.qip b/quartus/pll32.qip new file mode 100644 index 0000000..f78bd93 --- /dev/null +++ b/quartus/pll32.qip @@ -0,0 +1,4 @@ +set_global_assignment -name IP_TOOL_NAME "ALTPLL" +set_global_assignment -name IP_TOOL_VERSION "9.1" +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "pll32.vhd"] +set_global_assignment -name MISC_FILE [file join $::quartus(qip_path) "pll32.ppf"] diff --git a/quartus/pll32.vhd b/quartus/pll32.vhd new file mode 100644 index 0000000..43df1aa --- /dev/null +++ b/quartus/pll32.vhd @@ -0,0 +1,365 @@ +-- megafunction wizard: %ALTPLL% +-- GENERATION: STANDARD +-- VERSION: WM1.0 +-- MODULE: altpll + +-- ============================================================ +-- File Name: pll32.vhd +-- Megafunction Name(s): +-- altpll +-- +-- Simulation Library Files(s): +-- altera_mf +-- ============================================================ +-- ************************************************************ +-- THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +-- +-- 9.1 Build 222 10/21/2009 SJ Web Edition +-- ************************************************************ + + +--Copyright (C) 1991-2009 Altera Corporation +--Your use of Altera Corporation's design tools, logic functions +--and other software and tools, and its AMPP partner logic +--functions, and any output files from any of the foregoing +--(including device programming or simulation files), and any +--associated documentation or information are expressly subject +--to the terms and conditions of the Altera Program License +--Subscription Agreement, Altera MegaCore Function License +--Agreement, or other applicable license agreement, including, +--without limitation, that your use is for the sole purpose of +--programming logic devices manufactured by Altera and sold by +--Altera or its authorized distributors. Please refer to the +--applicable agreement for further details. + + +LIBRARY ieee; +USE ieee.std_logic_1164.all; + +LIBRARY altera_mf; +USE altera_mf.all; + +ENTITY pll32 IS + PORT + ( + areset : IN STD_LOGIC := '0'; + inclk0 : IN STD_LOGIC := '0'; + c0 : OUT STD_LOGIC ; + locked : OUT STD_LOGIC + ); +END pll32; + + +ARCHITECTURE SYN OF pll32 IS + + SIGNAL sub_wire0 : STD_LOGIC_VECTOR (5 DOWNTO 0); + SIGNAL sub_wire1 : STD_LOGIC ; + SIGNAL sub_wire2 : STD_LOGIC ; + SIGNAL sub_wire3 : STD_LOGIC ; + SIGNAL sub_wire4 : STD_LOGIC_VECTOR (1 DOWNTO 0); + SIGNAL sub_wire5_bv : BIT_VECTOR (0 DOWNTO 0); + SIGNAL sub_wire5 : STD_LOGIC_VECTOR (0 DOWNTO 0); + + + + COMPONENT altpll + GENERIC ( + clk0_divide_by : NATURAL; + clk0_duty_cycle : NATURAL; + clk0_multiply_by : NATURAL; + clk0_phase_shift : STRING; + compensate_clock : STRING; + gate_lock_signal : STRING; + inclk0_input_frequency : NATURAL; + intended_device_family : STRING; + invalid_lock_multiplier : NATURAL; + lpm_hint : STRING; + lpm_type : STRING; + operation_mode : STRING; + port_activeclock : STRING; + port_areset : STRING; + port_clkbad0 : STRING; + port_clkbad1 : STRING; + port_clkloss : STRING; + port_clkswitch : STRING; + port_configupdate : STRING; + port_fbin : STRING; + port_inclk0 : STRING; + port_inclk1 : STRING; + port_locked : STRING; + port_pfdena : STRING; + port_phasecounterselect : STRING; + port_phasedone : STRING; + port_phasestep : STRING; + port_phaseupdown : STRING; + port_pllena : STRING; + port_scanaclr : STRING; + port_scanclk : STRING; + port_scanclkena : STRING; + port_scandata : STRING; + port_scandataout : STRING; + port_scandone : STRING; + port_scanread : STRING; + port_scanwrite : STRING; + port_clk0 : STRING; + port_clk1 : STRING; + port_clk2 : STRING; + port_clk3 : STRING; + port_clk4 : STRING; + port_clk5 : STRING; + port_clkena0 : STRING; + port_clkena1 : STRING; + port_clkena2 : STRING; + port_clkena3 : STRING; + port_clkena4 : STRING; + port_clkena5 : STRING; + port_extclk0 : STRING; + port_extclk1 : STRING; + port_extclk2 : STRING; + port_extclk3 : STRING; + valid_lock_multiplier : NATURAL + ); + PORT ( + inclk : IN STD_LOGIC_VECTOR (1 DOWNTO 0); + locked : OUT STD_LOGIC ; + areset : IN STD_LOGIC ; + clk : OUT STD_LOGIC_VECTOR (5 DOWNTO 0) + ); + END COMPONENT; + +BEGIN + sub_wire5_bv(0 DOWNTO 0) <= "0"; + sub_wire5 <= To_stdlogicvector(sub_wire5_bv); + sub_wire1 <= sub_wire0(0); + c0 <= sub_wire1; + locked <= sub_wire2; + sub_wire3 <= inclk0; + sub_wire4 <= sub_wire5(0 DOWNTO 0) & sub_wire3; + + altpll_component : altpll + GENERIC MAP ( + clk0_divide_by => 3, + clk0_duty_cycle => 50, + clk0_multiply_by => 4, + clk0_phase_shift => "0", + compensate_clock => "CLK0", + gate_lock_signal => "NO", + inclk0_input_frequency => 41666, + intended_device_family => "Cyclone II", + invalid_lock_multiplier => 5, + lpm_hint => "CBX_MODULE_PREFIX=pll32", + lpm_type => "altpll", + operation_mode => "NORMAL", + port_activeclock => "PORT_UNUSED", + port_areset => "PORT_USED", + port_clkbad0 => "PORT_UNUSED", + port_clkbad1 => "PORT_UNUSED", + port_clkloss => "PORT_UNUSED", + port_clkswitch => "PORT_UNUSED", + port_configupdate => "PORT_UNUSED", + port_fbin => "PORT_UNUSED", + port_inclk0 => "PORT_USED", + port_inclk1 => "PORT_UNUSED", + port_locked => "PORT_USED", + port_pfdena => "PORT_UNUSED", + port_phasecounterselect => "PORT_UNUSED", + port_phasedone => "PORT_UNUSED", + port_phasestep => "PORT_UNUSED", + port_phaseupdown => "PORT_UNUSED", + port_pllena => "PORT_UNUSED", + port_scanaclr => "PORT_UNUSED", + port_scanclk => "PORT_UNUSED", + port_scanclkena => "PORT_UNUSED", + port_scandata => "PORT_UNUSED", + port_scandataout => "PORT_UNUSED", + port_scandone => "PORT_UNUSED", + port_scanread => "PORT_UNUSED", + port_scanwrite => "PORT_UNUSED", + port_clk0 => "PORT_USED", + port_clk1 => "PORT_UNUSED", + port_clk2 => "PORT_UNUSED", + port_clk3 => "PORT_UNUSED", + port_clk4 => "PORT_UNUSED", + port_clk5 => "PORT_UNUSED", + port_clkena0 => "PORT_UNUSED", + port_clkena1 => "PORT_UNUSED", + port_clkena2 => "PORT_UNUSED", + port_clkena3 => "PORT_UNUSED", + port_clkena4 => "PORT_UNUSED", + port_clkena5 => "PORT_UNUSED", + port_extclk0 => "PORT_UNUSED", + port_extclk1 => "PORT_UNUSED", + port_extclk2 => "PORT_UNUSED", + port_extclk3 => "PORT_UNUSED", + valid_lock_multiplier => 1 + ) + PORT MAP ( + inclk => sub_wire4, + areset => areset, + clk => sub_wire0, + locked => sub_wire2 + ); + + + +END SYN; + +-- ============================================================ +-- CNX file retrieval info +-- ============================================================ +-- Retrieval info: PRIVATE: ACTIVECLK_CHECK STRING "0" +-- Retrieval info: PRIVATE: BANDWIDTH STRING "1.000" +-- Retrieval info: PRIVATE: BANDWIDTH_FEATURE_ENABLED STRING "0" +-- Retrieval info: PRIVATE: BANDWIDTH_FREQ_UNIT STRING "MHz" +-- Retrieval info: PRIVATE: BANDWIDTH_PRESET STRING "Low" +-- Retrieval info: PRIVATE: BANDWIDTH_USE_AUTO STRING "1" +-- Retrieval info: PRIVATE: BANDWIDTH_USE_CUSTOM STRING "0" +-- Retrieval info: PRIVATE: BANDWIDTH_USE_PRESET STRING "0" +-- Retrieval info: PRIVATE: CLKBAD_SWITCHOVER_CHECK STRING "0" +-- Retrieval info: PRIVATE: CLKLOSS_CHECK STRING "0" +-- Retrieval info: PRIVATE: CLKSWITCH_CHECK STRING "1" +-- Retrieval info: PRIVATE: CNX_NO_COMPENSATE_RADIO STRING "0" +-- Retrieval info: PRIVATE: CREATE_CLKBAD_CHECK STRING "0" +-- Retrieval info: PRIVATE: CREATE_INCLK1_CHECK STRING "0" +-- Retrieval info: PRIVATE: CUR_DEDICATED_CLK STRING "c0" +-- Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "e0" +-- Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "7" +-- Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "1" +-- Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000" +-- Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "32.000000" +-- Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0" +-- Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0" +-- Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1" +-- Retrieval info: PRIVATE: GLOCKED_FEATURE_ENABLED STRING "1" +-- Retrieval info: PRIVATE: GLOCKED_MODE_CHECK STRING "0" +-- Retrieval info: PRIVATE: GLOCK_COUNTER_EDIT NUMERIC "1048575" +-- Retrieval info: PRIVATE: HAS_MANUAL_SWITCHOVER STRING "1" +-- Retrieval info: PRIVATE: INCLK0_FREQ_EDIT STRING "24.000" +-- Retrieval info: PRIVATE: INCLK0_FREQ_UNIT_COMBO STRING "MHz" +-- Retrieval info: PRIVATE: INCLK1_FREQ_EDIT STRING "100.000" +-- Retrieval info: PRIVATE: INCLK1_FREQ_EDIT_CHANGED STRING "1" +-- Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_CHANGED STRING "1" +-- Retrieval info: PRIVATE: INCLK1_FREQ_UNIT_COMBO STRING "MHz" +-- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone II" +-- Retrieval info: PRIVATE: INT_FEEDBACK__MODE_RADIO STRING "1" +-- Retrieval info: PRIVATE: LOCKED_OUTPUT_CHECK STRING "1" +-- Retrieval info: PRIVATE: LONG_SCAN_RADIO STRING "1" +-- Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "312.000" +-- Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0" +-- Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg" +-- Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any" +-- Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0" +-- Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "1" +-- Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "1" +-- Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "32.00000000" +-- Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "1" +-- Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz" +-- Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "0" +-- Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0" +-- Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "0.00000000" +-- Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0" +-- Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg" +-- Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0" +-- Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1" +-- Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1" +-- Retrieval info: PRIVATE: PLL_ENA_CHECK STRING "0" +-- Retrieval info: PRIVATE: PLL_ENHPLL_CHECK NUMERIC "0" +-- Retrieval info: PRIVATE: PLL_FASTPLL_CHECK NUMERIC "0" +-- Retrieval info: PRIVATE: PLL_FBMIMIC_CHECK STRING "0" +-- Retrieval info: PRIVATE: PLL_LVDS_PLL_CHECK NUMERIC "0" +-- Retrieval info: PRIVATE: PLL_PFDENA_CHECK STRING "0" +-- Retrieval info: PRIVATE: PLL_TARGET_HARCOPY_CHECK NUMERIC "0" +-- Retrieval info: PRIVATE: PRIMARY_CLK_COMBO STRING "inclk0" +-- Retrieval info: PRIVATE: RECONFIG_FILE STRING "pll32.mif" +-- Retrieval info: PRIVATE: SACN_INPUTS_CHECK STRING "0" +-- Retrieval info: PRIVATE: SCAN_FEATURE_ENABLED STRING "0" +-- Retrieval info: PRIVATE: SELF_RESET_LOCK_LOSS STRING "0" +-- Retrieval info: PRIVATE: SHORT_SCAN_RADIO STRING "0" +-- Retrieval info: PRIVATE: SPREAD_FEATURE_ENABLED STRING "0" +-- Retrieval info: PRIVATE: SPREAD_FREQ STRING "50.000" +-- Retrieval info: PRIVATE: SPREAD_FREQ_UNIT STRING "KHz" +-- Retrieval info: PRIVATE: SPREAD_PERCENT STRING "0.500" +-- Retrieval info: PRIVATE: SPREAD_USE STRING "0" +-- Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0" +-- Retrieval info: PRIVATE: STICKY_CLK0 STRING "1" +-- Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1" +-- Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1" +-- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +-- Retrieval info: PRIVATE: USE_CLK0 STRING "1" +-- Retrieval info: PRIVATE: USE_CLKENA0 STRING "0" +-- Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0" +-- Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0" +-- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +-- Retrieval info: CONSTANT: CLK0_DIVIDE_BY NUMERIC "3" +-- Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50" +-- Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "4" +-- Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0" +-- Retrieval info: CONSTANT: COMPENSATE_CLOCK STRING "CLK0" +-- Retrieval info: CONSTANT: GATE_LOCK_SIGNAL STRING "NO" +-- Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "41666" +-- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone II" +-- Retrieval info: CONSTANT: INVALID_LOCK_MULTIPLIER NUMERIC "5" +-- Retrieval info: CONSTANT: LPM_TYPE STRING "altpll" +-- Retrieval info: CONSTANT: OPERATION_MODE STRING "NORMAL" +-- Retrieval info: CONSTANT: PORT_ACTIVECLOCK STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_ARESET STRING "PORT_USED" +-- Retrieval info: CONSTANT: PORT_CLKBAD0 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_CLKBAD1 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_CLKLOSS STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_CLKSWITCH STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_CONFIGUPDATE STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_FBIN STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_INCLK0 STRING "PORT_USED" +-- Retrieval info: CONSTANT: PORT_INCLK1 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_LOCKED STRING "PORT_USED" +-- Retrieval info: CONSTANT: PORT_PFDENA STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_PHASECOUNTERSELECT STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_PHASEDONE STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_PHASESTEP STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_PHASEUPDOWN STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_PLLENA STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANACLR STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANCLK STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANCLKENA STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANDATA STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANDATAOUT STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANDONE STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED" +-- Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clk5 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clkena0 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clkena1 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clkena2 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clkena3 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clkena4 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_clkena5 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_extclk0 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_extclk1 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_extclk2 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: PORT_extclk3 STRING "PORT_UNUSED" +-- Retrieval info: CONSTANT: VALID_LOCK_MULTIPLIER NUMERIC "1" +-- Retrieval info: USED_PORT: @clk 0 0 6 0 OUTPUT_CLK_EXT VCC "@clk[5..0]" +-- Retrieval info: USED_PORT: @extclk 0 0 4 0 OUTPUT_CLK_EXT VCC "@extclk[3..0]" +-- Retrieval info: USED_PORT: @inclk 0 0 2 0 INPUT_CLK_EXT VCC "@inclk[1..0]" +-- Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset" +-- Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0" +-- Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0" +-- Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked" +-- Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0 +-- Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0 +-- Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0 +-- Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0 +-- Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0 +-- Retrieval info: GEN_FILE: TYPE_NORMAL pll32.vhd TRUE +-- Retrieval info: GEN_FILE: TYPE_NORMAL pll32.ppf TRUE +-- Retrieval info: GEN_FILE: TYPE_NORMAL pll32.inc FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL pll32.cmp FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL pll32.bsf FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL pll32_inst.vhd FALSE +-- Retrieval info: LIB_FILE: altera_mf +-- Retrieval info: CBX_MODULE_PREFIX: ON diff --git a/quartus/ps2_intf.vhd b/quartus/ps2_intf.vhd new file mode 100644 index 0000000..dc8440f --- /dev/null +++ b/quartus/ps2_intf.vhd @@ -0,0 +1,157 @@ +-- ZX Spectrum for Altera DE1 +-- +-- Copyright (c) 2009-2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +-- PS/2 interface (input only) +-- Based loosely on ps2_ctrl.vhd (c) ALSE. http://www.alse-fr.com +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +-- This is input-only for the time being +entity ps2_intf is +generic (filter_length : positive := 8); +port( + CLK : in std_logic; + nRESET : in std_logic; + + -- PS/2 interface (could be bi-dir) + PS2_CLK : in std_logic; + PS2_DATA : in std_logic; + + -- Byte-wide data interface - only valid for one clock + -- so must be latched externally if required + DATA : out std_logic_vector(7 downto 0); + VALID : out std_logic; + ERROR : out std_logic + ); +end ps2_intf; + +architecture ps2_intf_arch of ps2_intf is +subtype filter_t is std_logic_vector(filter_length-1 downto 0); +signal clk_filter : filter_t; + +signal ps2_clk_in : std_logic; +signal ps2_dat_in : std_logic; +-- Goes high when a clock falling edge is detected +signal clk_edge : std_logic; +signal bit_count : unsigned (3 downto 0); +signal shiftreg : std_logic_vector(8 downto 0); +signal parity : std_logic; +begin + -- Register input signals + process(nRESET,CLK) + begin + if nRESET = '0' then + ps2_clk_in <= '1'; + ps2_dat_in <= '1'; + clk_filter <= (others => '1'); + clk_edge <= '0'; + elsif rising_edge(CLK) then + -- Register inputs (and filter clock) + ps2_dat_in <= PS2_DATA; + clk_filter <= PS2_CLK & clk_filter(clk_filter'high downto 1); + clk_edge <= '0'; + + if clk_filter = filter_t'(others => '1') then + -- Filtered clock is high + ps2_clk_in <= '1'; + elsif clk_filter = filter_t'(others => '0') then + -- Filter clock is low, check for edge + if ps2_clk_in = '1' then + clk_edge <= '1'; + end if; + ps2_clk_in <= '0'; + end if; + end if; + end process; + + -- Shift in keyboard data + process(nRESET,CLK) + begin + if nRESET = '0' then + bit_count <= (others => '0'); + shiftreg <= (others => '0'); + parity <= '0'; + DATA <= (others => '0'); + VALID <= '0'; + ERROR <= '0'; + elsif rising_edge(CLK) then + -- Clear flags + VALID <= '0'; + ERROR <= '0'; + + if clk_edge = '1' then + -- We have a new bit from the keyboard for processing + if bit_count = 0 then + -- Idle state, check for start bit (0) only and don't + -- start counting bits until we get it + + parity <= '0'; + + if ps2_dat_in = '0' then + -- This is a start bit + bit_count <= bit_count + 1; + end if; + else + -- Running. 8-bit data comes in LSb first followed by + -- a single stop bit (1) + if bit_count < 10 then + -- Shift in data and parity (9 bits) + bit_count <= bit_count + 1; + shiftreg <= ps2_dat_in & shiftreg(shiftreg'high downto 1); + parity <= parity xor ps2_dat_in; -- Calculate parity + elsif ps2_dat_in = '1' then + -- Valid stop bit received + bit_count <= (others => '0'); -- back to idle + if parity = '1' then + -- Parity correct, submit data to host + DATA <= shiftreg(7 downto 0); + VALID <= '1'; + else + -- Error + ERROR <= '1'; + end if; + else + -- Invalid stop bit + bit_count <= (others => '0'); -- back to idle + ERROR <= '1'; + end if; + end if; + end if; + end if; + end process; +end ps2_intf_arch; diff --git a/quartus/roms/saa5050/genrom.c b/quartus/roms/saa5050/genrom.c new file mode 100644 index 0000000..9d93bb3 --- /dev/null +++ b/quartus/roms/saa5050/genrom.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include + +// Characters on a 5x9 matrix. This is padded up to 16 rows when the +// ROM is generated +static uint8_t saa5050_charset[] = { + 0,0,0,0,0,0,0,0,0, + 4,4,4,4,4,0,4,0,0, + 10,10,10,0,0,0,0,0,0, + 6,9,8,28,8,8,31,0,0, + 14,21,20,14,5,21,14,0,0, + 24,25,2,4,8,19,3,0,0, + 8,20,20,8,21,18,13,0,0, + 4,4,4,0,0,0,0,0,0, + + 2,4,8,8,8,4,2,0,0, + 8,4,2,2,2,4,8,0,0, + 4,21,14,4,14,21,4,0,0, + 0,4,4,31,4,4,0,0,0, + 0,0,0,0,0,4,4,8,0, + 0,0,0,14,0,0,0,0,0, + 0,0,0,0,0,0,4,0,0, + 0,1,2,4,8,16,0,0,0, + + 4,10,17,17,17,10,4,0,0, + 4,12,4,4,4,4,14,0,0, + 14,17,1,6,8,16,31,0,0, + 31,1,2,6,1,17,14,0,0, + 2,6,10,18,31,2,2,0,0, + 31,16,30,1,1,17,14,0,0, + 6,8,16,30,17,17,14,0,0, + 31,1,2,4,8,8,8,0,0, + + 14,17,17,14,17,17,14,0,0, + 14,17,17,15,1,2,12,0,0, + 0,0,4,0,0,0,4,0,0, + 0,0,4,0,0,4,4,8,0, + 2,4,8,16,8,4,2,0,0, + 0,0,31,0,31,0,0,0,0, + 8,4,2,1,2,4,8,0,0, + 14,17,2,4,4,0,4,0,0, + + 14,17,23,21,23,16,14,0,0, + 4,10,17,17,31,17,17,0,0, + 30,17,17,30,17,17,30,0,0, + 14,17,16,16,16,17,14,0,0, + 30,17,17,17,17,17,30,0,0, + 31,16,16,30,16,16,31,0,0, + 31,16,16,30,16,16,16,0,0, + 14,17,16,16,19,17,15,0,0, + + 17,17,17,31,17,17,17,0,0, + 14,4,4,4,4,4,14,0,0, + 1,1,1,1,1,17,14,0,0, + 17,18,20,24,20,18,17,0,0, + 16,16,16,16,16,16,31,0,0, + 17,27,21,21,17,17,17,0,0, + 17,17,25,21,19,17,17,0,0, + 14,17,17,17,17,17,14,0,0, + + 30,17,17,30,16,16,16,0,0, + 14,17,17,17,21,18,13,0,0, + 30,17,17,30,20,18,17,0,0, + 14,17,16,14,1,17,14,0,0, + 31,4,4,4,4,4,4,0,0, + 17,17,17,17,17,17,14,0,0, + 17,17,17,10,10,4,4,0,0, + 17,17,17,21,21,21,10,0,0, + + 17,17,10,4,10,17,17,0,0, + 17,17,10,4,4,4,4,0,0, + 31,1,2,4,8,16,31,0,0, + 0,4,8,31,8,4,0,0,0, + 16,16,16,16,22,1,2,4,7, + 0,4,2,31,2,4,0,0,0, + 0,4,14,21,4,4,0,0,0, + 10,10,31,10,31,10,10,0,0, + + 0,0,0,31,0,0,0,0,0, + 0,0,14,1,15,17,15,0,0, + 16,16,30,17,17,17,30,0,0, + 0,0,15,16,16,16,15,0,0, + 1,1,15,17,17,17,15,0,0, + 0,0,14,17,31,16,14,0,0, + 2,4,4,14,4,4,4,0,0, + 0,0,15,17,17,17,15,1,14, + + 16,16,30,17,17,17,17,0,0, + 4,0,12,4,4,4,14,0,0, + 4,0,4,4,4,4,4,4,8, + 8,8,9,10,12,10,9,0,0, + 12,4,4,4,4,4,14,0,0, + 0,0,26,21,21,21,21,0,0, + 0,0,30,17,17,17,17,0,0, + 0,0,14,17,17,17,14,0,0, + + 0,0,30,17,17,17,30,16,16, + 0,0,15,17,17,17,15,1,1, + 0,0,11,12,8,8,8,0,0, + 0,0,15,16,14,1,30,0,0, + 4,4,14,4,4,4,2,0,0, + 0,0,17,17,17,17,15,0,0, + 0,0,17,17,10,10,4,0,0, + 0,0,17,17,21,21,10,0,0, + + 0,0,17,10,4,10,17,0,0, + 0,0,17,17,17,17,15,1,14, + 0,0,31,2,4,8,31,0,0, + 8,8,8,8,9,3,5,7,1, + 10,10,10,10,10,10,10,0,0, + 24,4,24,4,25,3,5,7,1, + 0,4,0,31,0,4,0,0,0, + 31,31,31,31,31,31,31,0,0, +}; + +/* Generates 6x10 teletext graphics symbol according to specified code */ +void gen_gfx(char code,char *base) +{ + char *c; + int r; + + for (r = 0, c = base; r < 10; r++, c++) { + /* Set unused bit 7 to 1 to flag these characters as graphics (the + * implementation uses this to determine whether hold and separated + * modes should be applied */ + *c = 128; + if (r < 3) { + // 1 2 + if (code & 1) *c += 56; + if (code & 2) *c += 7; + } else if (r < 7) { + // 4 8 + if (code & 4) *c += 56; + if (code & 8) *c += 7; + } else { + // 16 64 + if (code & 16) *c += 56; + if (code & 64) *c += 7; + } + } +} + +int main(void) { + char *outbuf; + int ch,row; + + outbuf = malloc(256 * 16); + if (outbuf == NULL) { + fprintf(stderr,"Out of memory\n"); + return 1; + } + + /* Unused locations are blank (all zero) */ + memset(outbuf,0,256 * 16); + + /* Copy character bitmaps to locations 32-127 and 160-255 */ + for (ch = 0; ch < 96; ch++) { + for (row = 0; row < 9; row++) { + outbuf[16 * (32 + ch) + row + 1] = saa5050_charset[9 * ch + row]; + outbuf[16 * (160 + ch) + row + 1] = saa5050_charset[9 * ch + row]; + } + } + + /* Generate graphics in locations 160-191 and 224-255 */ + for (ch = 160; ch < 192; ch++) { + gen_gfx(ch,&outbuf[16 * ch]); + } + for (ch = 224; ch < 256; ch++) { + gen_gfx(ch,&outbuf[16 * ch]); + } + + /* Write to stdout */ + fwrite(outbuf,256 * 16,1,stdout); + + free(outbuf); + return 0; +} + diff --git a/quartus/roms/saa5050/saa5050.hex b/quartus/roms/saa5050/saa5050.hex new file mode 100644 index 0000000..f696b00 --- /dev/null +++ b/quartus/roms/saa5050/saa5050.hex @@ -0,0 +1,257 @@ +:1000000000000000000000000000000000000000F0 +:1000100000000000000000000000000000000000E0 +:1000200000000000000000000000000000000000D0 +:1000300000000000000000000000000000000000C0 +:1000400000000000000000000000000000000000B0 +:1000500000000000000000000000000000000000A0 +:100060000000000000000000000000000000000090 +:100070000000000000000000000000000000000080 +:100080000000000000000000000000000000000070 +:100090000000000000000000000000000000000060 +:1000A0000000000000000000000000000000000050 +:1000B0000000000000000000000000000000000040 +:1000C0000000000000000000000000000000000030 +:1000D0000000000000000000000000000000000020 +:1000E0000000000000000000000000000000000010 +:1000F0000000000000000000000000000000000000 +:1001000000000000000000000000000000000000EF +:1001100000000000000000000000000000000000DF +:1001200000000000000000000000000000000000CF +:1001300000000000000000000000000000000000BF +:1001400000000000000000000000000000000000AF +:10015000000000000000000000000000000000009F +:10016000000000000000000000000000000000008F +:10017000000000000000000000000000000000007F +:10018000000000000000000000000000000000006F +:10019000000000000000000000000000000000005F +:1001A000000000000000000000000000000000004F +:1001B000000000000000000000000000000000003F +:1001C000000000000000000000000000000000002F +:1001D000000000000000000000000000000000001F +:1001E000000000000000000000000000000000000F +:1001F00000000000000000000000000000000000FF +:1002000000000000000000000000000000000000EE +:1002100000040404040400040000000000000000C6 +:10022000000A0A0A000000000000000000000000B0 +:10023000000609081C08081F00000000000000005C +:10024000000E15140E05150E000000000000000041 +:100250000018190204081303000000000000000049 +:10026000000814140815120D000000000000000022 +:100270000004040400000000000000000000000072 +:10028000000204080808040200000000000000004A +:100290000008040202020408000000000000000040 +:1002A0000004150E040E15040000000000000000FC +:1002B000000004041F04040000000000000000000F +:1002C000000000000000040408000000000000001E +:1002D000000000000E000000000000000000000010 +:1002E000000000000000000400000000000000000A +:1002F00000000102040810000000000000000000DF +:1003000000040A1111110A0400000000000000009E +:1003100000040C040404040E0000000000000000AF +:10032000000E11010608101F000000000000000070 +:10033000001F01020601110E000000000000000075 +:100340000002060A121F0202000000000000000066 +:10035000001F101E0101110E00000000000000002F +:10036000000608101E11110E000000000000000021 +:10037000001F01020408080800000000000000003F +:10038000000E11110E11110E0000000000000000FF +:10039000000E11110F01020C00000000000000000F +:1003A0000000000400000004000000000000000045 +:1003B0000000000400000404080000000000000029 +:1003C0000002040810080402000000000000000001 +:1003D0000000001F001F00000000000000000000DF +:1003E00000080402010204080000000000000000F0 +:1003F000000E1102040400040000000000000000D0 +:10040000000E11171517100E00000000000000006C +:1004100000040A11111F111100000000000000006B +:10042000001E11111E11111E00000000000000002E +:10043000000E11101010110E00000000000000004E +:10044000001E11111111111E00000000000000001B +:10045000001F10101E10101F000000000000000000 +:10046000001F10101E1010100000000000000000FF +:10047000000E11101013110F00000000000000000A +:10048000001111111F1111110000000000000000E7 +:10049000000E04040404040E00000000000000002C +:1004A000000101010101110E000000000000000028 +:1004B00000111214181412110000000000000000B6 +:1004C000001010101010101F0000000000000000AD +:1004D00000111B1515111111000000000000000093 +:1004E0000011111915131111000000000000000087 +:1004F000000E11111111110E00000000000000008B +:10050000001E11111E10101000000000000000005D +:10051000000E11111115120D000000000000000066 +:10052000001E11111E141211000000000000000036 +:10053000000E11100E01110E00000000000000005E +:10054000001F040404040404000000000000000074 +:10055000001111111111110E000000000000000027 +:10056000001111110A0A040400000000000000003C +:10057000001111111515150A0000000000000000FF +:100580000011110A040A111100000000000000000F +:100590000011110A0404040400000000000000001F +:1005A000001F01020408101F0000000000000000EE +:1005B000000004081F080400000000000000000004 +:1005C00000101010101601020407000000000000C7 +:1005D000000004021F0204000000000000000000F0 +:1005E0000000040E150404000000000000000000DC +:1005F000000A0A1F0A1F0A0A00000000000000008B +:10060000000000001F0000000000000000000000CB +:100610000000000E010F110F00000000000000009C +:100620000010101E1111111E00000000000000003B +:100630000000000F1010100F00000000000000006C +:100640000001010F1111110F000000000000000057 +:100650000000000E111F100E00000000000000003E +:10066000000204040E040404000000000000000066 +:100670000000000F1111110F010E0000000000001A +:100680000010101E111111110000000000000000E8 +:100690000004000C0404040E000000000000000030 +:1006A0000004000404040404040800000000000026 +:1006B000000808090A0C0A090000000000000000F8 +:1006C000000C04040404040E0000000000000000FC +:1006D0000000001A151515150000000000000000AC +:1006E0000000001E111111110000000000000000A8 +:1006F0000000000E1111110E0000000000000000AB +:100700000000001E1111111E10100000000000005A +:100710000000000F1111110F010100000000000086 +:100720000000000B0C08080800000000000000009A +:100730000000000F100E011E00000000000000006D +:100740000004040E04040402000000000000000085 +:10075000000000111111110F000000000000000046 +:1007600000000011110A0A0400000000000000004F +:10077000000000111115150A000000000000000023 +:10078000000000110A040A1100000000000000002F +:10079000000000111111110F010E000000000000F7 +:1007A0000000001F0204081F0000000000000000FD +:1007B0000008080808090305070100000000000000 +:1007C000000A0A0A0A0A0A0A0000000000000000E3 +:1007D00000180418041903050701000000000000B8 +:1007E000000004001F0004000000000000000000E2 +:1007F000001F1F1F1F1F1F1F000000000000000020 +:1008000000000000000000000000000000000000E8 +:1008100000000000000000000000000000000000D8 +:1008200000000000000000000000000000000000C8 +:1008300000000000000000000000000000000000B8 +:1008400000000000000000000000000000000000A8 +:100850000000000000000000000000000000000098 +:100860000000000000000000000000000000000088 +:100870000000000000000000000000000000000078 +:100880000000000000000000000000000000000068 +:100890000000000000000000000000000000000058 +:1008A0000000000000000000000000000000000048 +:1008B0000000000000000000000000000000000038 +:1008C0000000000000000000000000000000000028 +:1008D0000000000000000000000000000000000018 +:1008E0000000000000000000000000000000000008 +:1008F00000000000000000000000000000000000F8 +:1009000000000000000000000000000000000000E7 +:1009100000000000000000000000000000000000D7 +:1009200000000000000000000000000000000000C7 +:1009300000000000000000000000000000000000B7 +:1009400000000000000000000000000000000000A7 +:100950000000000000000000000000000000000097 +:100960000000000000000000000000000000000087 +:100970000000000000000000000000000000000077 +:100980000000000000000000000000000000000067 +:100990000000000000000000000000000000000057 +:1009A0000000000000000000000000000000000047 +:1009B0000000000000000000000000000000000037 +:1009C0000000000000000000000000000000000027 +:1009D0000000000000000000000000000000000017 +:1009E0000000000000000000000000000000000007 +:1009F00000000000000000000000000000000000F7 +:100A000080808080808080808080000000000000E6 +:100A1000B8B8B8808080808080800000000000002E +:100A200087878780808080808080000000000000B1 +:100A3000BFBFBF80808080808080000000000000F9 +:100A4000808080B8B8B8B8808080000000000000C6 +:100A5000B8B8B8B8B8B8B88080800000000000000E +:100A6000878787B8B8B8B880808000000000000091 +:100A7000BFBFBFB8B8B8B8808080000000000000D9 +:100A8000808080878787878080800000000000004A +:100A9000B8B8B88787878780808000000000000092 +:100AA0008787878787878780808000000000000015 +:100AB000BFBFBF878787878080800000000000005D +:100AC000808080BFBFBFBF8080800000000000002A +:100AD000B8B8B8BFBFBFBF80808000000000000072 +:100AE000878787BFBFBFBF808080000000000000F5 +:100AF000BFBFBFBFBFBFBF8080800000000000003D +:100B000080808080808080B8B8B80000000000003D +:100B1000B8B8B880808080B8B8B800000000000085 +:100B200087878780808080B8B8B800000000000008 +:100B3000BFBFBF80808080B8B8B800000000000050 +:100B4000808080B8B8B8B8B8B8B80000000000001D +:100B5000B8B8B8B8B8B8B8B8B8B800000000000065 +:100B6000878787B8B8B8B8B8B8B8000000000000E8 +:100B7000BFBFBFB8B8B8B8B8B8B800000000000030 +:100B800080808087878787B8B8B8000000000000A1 +:100B9000B8B8B887878787B8B8B8000000000000E9 +:100BA00087878787878787B8B8B80000000000006C +:100BB000BFBFBF87878787B8B8B8000000000000B4 +:100BC000808080BFBFBFBFB8B8B800000000000081 +:100BD000B8B8B8BFBFBFBFB8B8B8000000000000C9 +:100BE000878787BFBFBFBFB8B8B80000000000004C +:100BF000BFBFBFBFBFBFBFB8B8B800000000000094 +:100C0000000E11171517100E000000000000000064 +:100C100000040A11111F1111000000000000000063 +:100C2000001E11111E11111E000000000000000026 +:100C3000000E11101010110E000000000000000046 +:100C4000001E11111111111E000000000000000013 +:100C5000001F10101E10101F0000000000000000F8 +:100C6000001F10101E1010100000000000000000F7 +:100C7000000E11101013110F000000000000000002 +:100C8000001111111F1111110000000000000000DF +:100C9000000E04040404040E000000000000000024 +:100CA000000101010101110E000000000000000020 +:100CB00000111214181412110000000000000000AE +:100CC000001010101010101F0000000000000000A5 +:100CD00000111B151511111100000000000000008B +:100CE000001111191513111100000000000000007F +:100CF000000E11111111110E000000000000000083 +:100D0000001E11111E101010000000000000000055 +:100D1000000E11111115120D00000000000000005E +:100D2000001E11111E14121100000000000000002E +:100D3000000E11100E01110E000000000000000056 +:100D4000001F04040404040400000000000000006C +:100D5000001111111111110E00000000000000001F +:100D6000001111110A0A0404000000000000000034 +:100D7000001111111515150A0000000000000000F7 +:100D80000011110A040A1111000000000000000007 +:100D90000011110A04040404000000000000000017 +:100DA000001F01020408101F0000000000000000E6 +:100DB000000004081F0804000000000000000000FC +:100DC00000101010101601020407000000000000BF +:100DD000000004021F0204000000000000000000E8 +:100DE0000000040E150404000000000000000000D4 +:100DF000000A0A1F0A1F0A0A000000000000000083 +:100E000080808080808080878787000000000000CD +:100E1000B8B8B88080808087878700000000000015 +:100E20008787878080808087878700000000000098 +:100E3000BFBFBF80808080878787000000000000E0 +:100E4000808080B8B8B8B8878787000000000000AD +:100E5000B8B8B8B8B8B8B8878787000000000000F5 +:100E6000878787B8B8B8B887878700000000000078 +:100E7000BFBFBFB8B8B8B8878787000000000000C0 +:100E80008080808787878787878700000000000031 +:100E9000B8B8B88787878787878700000000000079 +:100EA00087878787878787878787000000000000FC +:100EB000BFBFBF8787878787878700000000000044 +:100EC000808080BFBFBFBF87878700000000000011 +:100ED000B8B8B8BFBFBFBF87878700000000000059 +:100EE000878787BFBFBFBF878787000000000000DC +:100EF000BFBFBFBFBFBFBF87878700000000000024 +:100F000080808080808080BFBFBF00000000000024 +:100F1000B8B8B880808080BFBFBF0000000000006C +:100F200087878780808080BFBFBF000000000000EF +:100F3000BFBFBF80808080BFBFBF00000000000037 +:100F4000808080B8B8B8B8BFBFBF00000000000004 +:100F5000B8B8B8B8B8B8B8BFBFBF0000000000004C +:100F6000878787B8B8B8B8BFBFBF000000000000CF +:100F7000BFBFBFB8B8B8B8BFBFBF00000000000017 +:100F800080808087878787BFBFBF00000000000088 +:100F9000B8B8B887878787BFBFBF000000000000D0 +:100FA00087878787878787BFBFBF00000000000053 +:100FB000BFBFBF87878787BFBFBF0000000000009B +:100FC000808080BFBFBFBFBFBFBF00000000000068 +:100FD000B8B8B8BFBFBFBFBFBFBF000000000000B0 +:100FE000878787BFBFBFBFBFBFBF00000000000033 +:100FF000BFBFBFBFBFBFBFBFBFBF0000000000007B +:00000001FF diff --git a/quartus/roms/saa5050/saa5050.rom b/quartus/roms/saa5050/saa5050.rom new file mode 100644 index 0000000..71565e3 Binary files /dev/null and b/quartus/roms/saa5050/saa5050.rom differ diff --git a/quartus/saa5050.vhd b/quartus/saa5050.vhd new file mode 100644 index 0000000..57dd999 --- /dev/null +++ b/quartus/saa5050.vhd @@ -0,0 +1,391 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- SAA5050 teletext generator +-- +-- Synchronous implementation for FPGA. Certain TV-specific functions are +-- not implemented. e.g. +-- +-- No /SI pin - 'TEXT' mode is permanently enabled +-- No remote control features (/DATA, DLIM) +-- No large character support +-- No support for box overlay (BLAN, PO, DE) +-- No character rounding, although this may be added +-- +-- FIXME: Hold graphics not supported - this needs to be added +-- +-- (C) 2011 Mike Stirling +-- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity saa5050 is +port ( + CLOCK : in std_logic; + -- 6 MHz dot clock enable + CLKEN : in std_logic; + -- Async reset + nRESET : in std_logic; + + -- Character data input (in the bus clock domain) + DI_CLOCK : in std_logic; + DI_CLKEN : in std_logic; + DI : in std_logic_vector(6 downto 0); + + -- Timing inputs + -- General line reset (not used) + GLR : in std_logic; -- /HSYNC + -- Data entry window - high during VSYNC. + -- Resets ROM row counter and drives 'flash' signal + DEW : in std_logic; -- VSYNC + -- Character rounding select - high during even field + CRS : in std_logic; -- FIELD + -- Load output shift register enable - high during active video + LOSE : in std_logic; -- DE + + -- Video out + R : out std_logic; + G : out std_logic; + B : out std_logic; + Y : out std_logic + ); +end entity; + +architecture rtl of saa5050 is + +component saa5050_rom IS + PORT + ( + address : IN STD_LOGIC_VECTOR (11 DOWNTO 0); + clock : IN STD_LOGIC ; + q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0) + ); +end component; + +-- Register inputs in the bus clock domain +signal di_r : std_logic_vector(6 downto 0); +signal dew_r : std_logic; +signal lose_r : std_logic; +-- Data input registered in the pixel clock domain +signal code : std_logic_vector(6 downto 0); +signal line_addr : unsigned(3 downto 0); +signal rom_address : std_logic_vector(11 downto 0); +signal rom_data : std_logic_vector(7 downto 0); + +-- Delayed display enable derived from LOSE by delaying for one character +signal disp_enable : std_logic; +-- Latched timing signals for detection of falling edges +signal dew_latch : std_logic; +signal lose_latch : std_logic; +signal disp_enable_latch : std_logic; + +-- Row and column addressing is handled externally. We just need to +-- keep track of which of the 10 lines we are on within the character... +signal line_counter : unsigned(3 downto 0); +-- ... and which of the 6 pixels we are on within each line +signal pixel_counter : unsigned(2 downto 0); +-- We also need to count frames to implement the flash feature. +-- The datasheet says this is 0.75 Hz with a 3:1 on/off ratio, so it +-- is probably a /64 counter, which gives us 0.78 Hz +signal flash_counter : unsigned(5 downto 0); +-- Output shift register +signal shift_reg : std_logic_vector(5 downto 0); + +-- Flash mask +signal flash : std_logic; + +-- Current display state +-- Foreground colour (B2, G1, R0) +signal fg : std_logic_vector(2 downto 0); +-- Background colour (B2, G1, R0) +signal bg : std_logic_vector(2 downto 0); +signal conceal : std_logic; +signal gfx : std_logic; +signal gfx_sep : std_logic; +signal gfx_hold : std_logic; +signal is_flash : std_logic; +signal double_high : std_logic; +-- Set in first row of double height +signal double_high1 : std_logic; +-- Set in second row of double height +signal double_high2 : std_logic; + +begin + char_rom: saa5050_rom port map ( + rom_address, + CLOCK, + rom_data + ); + + -- Generate flash signal for 3:1 ratio + flash <= flash_counter(5) and flash_counter(4); + + -- Sync inputs + process(DI_CLOCK,nRESET) + begin + if nRESET = '0' then + di_r <= (others => '0'); + dew_r <= '0'; + lose_r <= '0'; + elsif rising_edge(DI_CLOCK) and DI_CLKEN = '1' then + di_r <= DI; + dew_r <= DEW; + lose_r <= LOSE; + end if; + end process; + + -- Register data into pixel clock domain + process(CLOCK,nRESET) + begin + if nRESET = '0' then + code <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + code <= di_r; + end if; + end process; + + -- Generate character rom address in pixel clock domain + -- This is done combinatorially since all the inputs are already + -- registered and the address is re-registered by the ROM + line_addr <= line_counter when double_high = '0' else + ("0" & line_counter(3 downto 1)) when double_high2 = '0' else + ("0" & line_counter(3 downto 1)) + 5; + rom_address <= (others => '0') when (double_high = '0' and double_high2 = '1') else + gfx & code & std_logic_vector(line_addr); + + -- Character row and pixel counters + process(CLOCK,nRESET) + begin + if nRESET = '0' then + dew_latch <= '0'; + lose_latch <= '0'; + disp_enable <= '0'; + disp_enable_latch <= '0'; + double_high1 <= '0'; + double_high2 <= '0'; + line_counter <= (others => '0'); + pixel_counter <= (others => '0'); + flash_counter <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Register syncs for edge detection + dew_latch <= dew_r; + lose_latch <= lose_r; + disp_enable_latch <= disp_enable; + + -- When first entering double-height mode start on top row + if double_high = '1' and double_high1 = '0' and double_high2 = '0' then + double_high1 <= '1'; + end if; + + -- Count pixels between 0 and 5 + if pixel_counter = 5 then + -- Start of next character and delayed display enable + pixel_counter <= (others => '0'); + disp_enable <= lose_latch; + else + pixel_counter <= pixel_counter + 1; + end if; + + -- Rising edge of LOSE is the start of the active line + if lose_r = '1' and lose_latch = '0' then + -- Reset pixel counter - small offset to make the output + -- line up with the cursor from the video ULA + pixel_counter <= "011"; + end if; + + -- Count frames on end of VSYNC (falling edge of DEW) + if dew_r = '0' and dew_latch = '1' then + flash_counter <= flash_counter + 1; + end if; + + if dew_r = '1' then + -- Reset line counter and double height state during VSYNC + line_counter <= (others => '0'); + double_high1 <= '0'; + double_high2 <= '0'; + else + -- Count lines on end of active video (falling edge of disp_enable) + if disp_enable = '0' and disp_enable_latch = '1' then + if line_counter = 9 then + line_counter <= (others => '0'); + + -- Keep track of which row we are on for double-height + -- The double_high flag can be cleared before the end of a row, but if + -- double height characters are used anywhere on a row then the double_high1 + -- flag will be set and remain set until the next row. This is used + -- to determine that the bottom half of the characters should be shown if + -- double_high is set once again on the row below. + double_high1 <= '0'; + double_high2 <= double_high1; + else + line_counter <= line_counter + 1; + end if; + end if; + end if; + end if; + end process; + + -- Shift register + process(CLOCK,nRESET) + begin + if nRESET = '0' then + shift_reg <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + if disp_enable = '1' and pixel_counter = 0 then + -- Load the shift register with the ROM bit pattern + -- at the start of each character while disp_enable is asserted. + shift_reg <= rom_data(5 downto 0); + + -- If bit 7 of the ROM data is set then this is a graphics + -- character and separated/hold graphics modes apply. + -- We don't just assume this to be the case if gfx=1 because + -- these modes don't apply to caps even in graphics mode + if rom_data(7) = '1' then + -- Apply a mask for separated graphics mode + if gfx_sep = '1' then + shift_reg(5) <= '0'; + shift_reg(2) <= '0'; + if line_counter = 2 or line_counter = 6 or line_counter = 9 then + shift_reg <= (others => '0'); + end if; + end if; + end if; + else + -- Pump the shift register + shift_reg <= shift_reg(4 downto 0) & "0"; + end if; + end if; + end process; + + -- Control character handling + process(CLOCK,nRESET) + begin + if nRESET = '0' then + fg <= (others => '1'); + bg <= (others => '0'); + conceal <= '0'; + gfx <= '0'; + gfx_sep <= '0'; + gfx_hold <= '0'; + is_flash <= '0'; + double_high <= '0'; + elsif rising_edge(CLOCK) and CLKEN = '1' then + if disp_enable = '0' then + -- Reset to start of line defaults + fg <= (others => '1'); + bg <= (others => '0'); + conceal <= '0'; + gfx <= '0'; + gfx_sep <= '0'; + gfx_hold <= '0'; + is_flash <= '0'; + double_high <= '0'; + elsif pixel_counter = 0 then + -- Latch new control codes at the start of each character + if code(6 downto 5) = "00" then + if code(3) = '0' then + -- Colour and graphics setting clears conceal mode + conceal <= '0'; + + -- Select graphics or alpha mode + gfx <= code(4); + + -- 0 would be black but is not allowed so has no effect, + -- otherwise set the colour + if code(2 downto 0) /= "000" then + fg <= code(2 downto 0); + end if; + else + case code(4 downto 0) is + -- FLASH + when "01000" => is_flash <= '1'; + -- STEADY + when "01001" => is_flash <= '0'; + -- NORMAL HEIGHT + when "01100" => double_high <= '0'; + -- DOUBLE HEIGHT + when "01101" => double_high <= '1'; + -- CONCEAL + when "11000" => conceal <= '1'; + -- CONTIGUOUS GFX + when "11001" => gfx_sep <= '0'; + -- SEPARATED GFX + when "11010" => gfx_sep <= '1'; + -- BLACK BACKGROUND + when "11100" => bg <= (others => '0'); + -- NEW BACKGROUND + when "11101" => bg <= fg; + -- HOLD GFX + when "11110" => gfx_hold <= '1'; + -- RELEASE GFX + when "11111" => gfx_hold <= '0'; + + when others => null; + end case; + end if; + end if; + end if; + end if; + end process; + + -- Output + process(CLOCK,nRESET) + variable pixel : std_logic; + begin + pixel := shift_reg(5) and not ((flash and is_flash) or conceal); + + if nRESET = '0' then + R <= '0'; + G <= '0'; + B <= '0'; + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Generate mono output + Y <= pixel; + + -- Generate colour output + if pixel = '1' then + R <= fg(0); + G <= fg(1); + B <= fg(2); + else + R <= bg(0); + G <= bg(1); + B <= bg(2); + end if; + end if; + end process; +end architecture; diff --git a/quartus/saa5050_rom.qip b/quartus/saa5050_rom.qip new file mode 100644 index 0000000..e9513f7 --- /dev/null +++ b/quartus/saa5050_rom.qip @@ -0,0 +1,3 @@ +set_global_assignment -name IP_TOOL_NAME "ROM: 1-PORT" +set_global_assignment -name IP_TOOL_VERSION "9.1" +set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "saa5050_rom.vhd"] diff --git a/quartus/saa5050_rom.vhd b/quartus/saa5050_rom.vhd new file mode 100644 index 0000000..7541c0b --- /dev/null +++ b/quartus/saa5050_rom.vhd @@ -0,0 +1,168 @@ +-- megafunction wizard: %ROM: 1-PORT% +-- GENERATION: STANDARD +-- VERSION: WM1.0 +-- MODULE: altsyncram + +-- ============================================================ +-- File Name: saa5050_rom.vhd +-- Megafunction Name(s): +-- altsyncram +-- +-- Simulation Library Files(s): +-- altera_mf +-- ============================================================ +-- ************************************************************ +-- THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE! +-- +-- 9.1 Build 222 10/21/2009 SJ Web Edition +-- ************************************************************ + + +--Copyright (C) 1991-2009 Altera Corporation +--Your use of Altera Corporation's design tools, logic functions +--and other software and tools, and its AMPP partner logic +--functions, and any output files from any of the foregoing +--(including device programming or simulation files), and any +--associated documentation or information are expressly subject +--to the terms and conditions of the Altera Program License +--Subscription Agreement, Altera MegaCore Function License +--Agreement, or other applicable license agreement, including, +--without limitation, that your use is for the sole purpose of +--programming logic devices manufactured by Altera and sold by +--Altera or its authorized distributors. Please refer to the +--applicable agreement for further details. + + +LIBRARY ieee; +USE ieee.std_logic_1164.all; + +LIBRARY altera_mf; +USE altera_mf.all; + +ENTITY saa5050_rom IS + PORT + ( + address : IN STD_LOGIC_VECTOR (11 DOWNTO 0); + clock : IN STD_LOGIC ; + q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0) + ); +END saa5050_rom; + + +ARCHITECTURE SYN OF saa5050_rom IS + + SIGNAL sub_wire0 : STD_LOGIC_VECTOR (7 DOWNTO 0); + + + + COMPONENT altsyncram + GENERIC ( + clock_enable_input_a : STRING; + clock_enable_output_a : STRING; + init_file : STRING; + intended_device_family : STRING; + lpm_hint : STRING; + lpm_type : STRING; + numwords_a : NATURAL; + operation_mode : STRING; + outdata_aclr_a : STRING; + outdata_reg_a : STRING; + widthad_a : NATURAL; + width_a : NATURAL; + width_byteena_a : NATURAL + ); + PORT ( + clock0 : IN STD_LOGIC ; + address_a : IN STD_LOGIC_VECTOR (11 DOWNTO 0); + q_a : OUT STD_LOGIC_VECTOR (7 DOWNTO 0) + ); + END COMPONENT; + +BEGIN + q <= sub_wire0(7 DOWNTO 0); + + altsyncram_component : altsyncram + GENERIC MAP ( + clock_enable_input_a => "BYPASS", + clock_enable_output_a => "BYPASS", + init_file => "./roms/saa5050/saa5050.hex", + intended_device_family => "Cyclone II", + lpm_hint => "ENABLE_RUNTIME_MOD=NO", + lpm_type => "altsyncram", + numwords_a => 4096, + operation_mode => "ROM", + outdata_aclr_a => "NONE", + outdata_reg_a => "UNREGISTERED", + widthad_a => 12, + width_a => 8, + width_byteena_a => 1 + ) + PORT MAP ( + clock0 => clock, + address_a => address, + q_a => sub_wire0 + ); + + + +END SYN; + +-- ============================================================ +-- CNX file retrieval info +-- ============================================================ +-- Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0" +-- Retrieval info: PRIVATE: AclrAddr NUMERIC "0" +-- Retrieval info: PRIVATE: AclrByte NUMERIC "0" +-- Retrieval info: PRIVATE: AclrOutput NUMERIC "0" +-- Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0" +-- Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8" +-- Retrieval info: PRIVATE: BlankMemory NUMERIC "0" +-- Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0" +-- Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0" +-- Retrieval info: PRIVATE: Clken NUMERIC "0" +-- Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0" +-- Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A" +-- Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0" +-- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone II" +-- Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0" +-- Retrieval info: PRIVATE: JTAG_ID STRING "NONE" +-- Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0" +-- Retrieval info: PRIVATE: MIFfilename STRING "./roms/saa5050/saa5050.hex" +-- Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "4096" +-- Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0" +-- Retrieval info: PRIVATE: RegAddr NUMERIC "1" +-- Retrieval info: PRIVATE: RegOutput NUMERIC "0" +-- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0" +-- Retrieval info: PRIVATE: SingleClock NUMERIC "1" +-- Retrieval info: PRIVATE: UseDQRAM NUMERIC "0" +-- Retrieval info: PRIVATE: WidthAddr NUMERIC "12" +-- Retrieval info: PRIVATE: WidthData NUMERIC "8" +-- Retrieval info: PRIVATE: rden NUMERIC "0" +-- Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS" +-- Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS" +-- Retrieval info: CONSTANT: INIT_FILE STRING "./roms/saa5050/saa5050.hex" +-- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone II" +-- Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO" +-- Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram" +-- Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "4096" +-- Retrieval info: CONSTANT: OPERATION_MODE STRING "ROM" +-- Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE" +-- Retrieval info: CONSTANT: OUTDATA_REG_A STRING "UNREGISTERED" +-- Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "12" +-- Retrieval info: CONSTANT: WIDTH_A NUMERIC "8" +-- Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1" +-- Retrieval info: USED_PORT: address 0 0 12 0 INPUT NODEFVAL address[11..0] +-- Retrieval info: USED_PORT: clock 0 0 0 0 INPUT NODEFVAL clock +-- Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL q[7..0] +-- Retrieval info: CONNECT: @address_a 0 0 12 0 address 0 0 12 0 +-- Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0 +-- Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0 +-- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.vhd TRUE +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.inc FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.cmp FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.bsf FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom_inst.vhd FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom_waveforms.html FALSE +-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom_wave*.jpg FALSE +-- Retrieval info: LIB_FILE: altera_mf diff --git a/quartus/seg7.vhd b/quartus/seg7.vhd new file mode 100644 index 0000000..a61e679 --- /dev/null +++ b/quartus/seg7.vhd @@ -0,0 +1,68 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2009-2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- + +library ieee; +use ieee.std_logic_1164.all; + +-- Convert BCD to 7-segment display characters +entity seg7 is +port ( + D : in std_logic_vector(3 downto 0); + Q : out std_logic_vector(6 downto 0) +); +end seg7; + +architecture seg7_arch of seg7 is +begin + Q <= "1000000" when D = "0000" else + "1111001" when D = "0001" else + "0100100" when D = "0010" else + "0110000" when D = "0011" else + "0011001" when D = "0100" else + "0010010" when D = "0101" else + "0000010" when D = "0110" else + "1111000" when D = "0111" else + "0000000" when D = "1000" else + "0010000" when D = "1001" else + "0001000" when D = "1010" else + "0000011" when D = "1011" else + "1000110" when D = "1100" else + "0100001" when D = "1101" else + "0000110" when D = "1110" else + "0001110"; +end seg7_arch; + diff --git a/quartus/sn76489-1.0/COPYING b/quartus/sn76489-1.0/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/quartus/sn76489-1.0/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/quartus/sn76489-1.0/README b/quartus/sn76489-1.0/README new file mode 100644 index 0000000..3363014 --- /dev/null +++ b/quartus/sn76489-1.0/README @@ -0,0 +1,143 @@ + +An SN76489AN Compatible Implementation in VHDL +============================================== +Version: $Date: 2006/06/18 19:28:40 $ + +Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +See the file COPYING. + + +Integration +----------- + +The sn76489 design exhibits all interface signals as the original chip. It +only differs in the audio data output which is provided as an 8 bit signed +vector instead of an analog output pin. + + generic ( + clock_div_16_g : integer := 1 + -- Set to '1' when operating the design in SN76489 mode. The primary clock + -- input is divided by 16 in this variant. The data sheet mentions the + -- SN76494 which contains a divide-by-2 clock input stage. Set the generic + -- to '0' to enable this mode. + ); + port ( + clock_i : in std_logic; + -- Primary clock input + -- Drive with the target frequency or any integer multiple of it. + + clock_en_i : in std_logic; + -- Clock enable + -- A '1' on this input qualifies a valid rising edge on clock_i. A '0' + -- disables the next rising clock edge, effectivley halting the design + -- until the next enabled rising clock edge. + -- Can be used to run the core at lower frequencies than applied on + -- clock_i. + + res_n_i : in std_logic; + -- Asynchronous low active reset input. + -- Sets all sequential elements to a known state. + + ce_n_i : in std_logic; + -- Chip enable, low active. + + we_n_i : in std_logic; + -- Write enable, low active. + + ready_o : out std_logic; + -- Ready indication to microprocessor. + + d_i : in std_logic_vector(0 to 7); + -- Data input + -- MSB 0 ... 7 LSB + + aout_o : out signed(0 to 7) + -- Audio output, signed vector + -- MSB/SIGN 0 ... 7 LSB + ); + + +Both 8 bit vector ports are defined (0 to 7) which declares bit 0 to be the +MSB and bit 7 to be the LSB. This has been implemented according to TI's data +sheet, thus all register/data format figures apply 1:1 for this design. +Many systems will flip the system data bus bit wise before it is connected to +this PSG. This is simply achieved with the following VHDL construct: + + signal data_s : std_logic_vector(7 downto 0); + + ... + d_i => data_s, + ... + +d_i and data_s will be assigned from left to right, resulting in the expected +bit assignment: + + d_i data_s + 0 7 + 1 6 + ... + 6 1 + 7 0 + + +As this design is fully synchronous, care has to be taken when the design +replaces an SN76489 in asynchronous mode. No problems are expected when +interfacing the code to other synchronous components. + + +Design Hierarchy +---------------- + + sn76489_top + | + +-- sn76489_latch_ctrl + | + +-- sn76489_clock_div + | + +-- sn76489_tone + | | + | \-- sn76489_attentuator + | + +-- sn76489_tone + | | + | \-- sn76489_attentuator + | + +-- sn76489_tone + | | + | \-- sn76489_attentuator + | + \-- sn76489_noise + | + \-- sn76489_attentuator + +Resulting compilation sequence: + + sn76489_comp_pack-p.vhd + sn76489_top.vhd + sn76489_latch_ctrl.vhd + sn76489_latch_ctrl-c.vhd + sn76489_clock_div.vhd + sn76489_clock_div-c.vhd + sn76489_attenuator.vhd + sn76489_attenuator-c.vhd + sn76489_tone.vhd + sn76489_tone-c.vhd + sn76489_noise.vhd + sn76489_noise-c.vhd + sn76489_top-c.vhd + +Skip the files containing VHDL configurations when analyzing the code for +synthesis. + + +References +---------- + +* TI Data sheet SN76489.pdf + ftp://ftp.whtech.com/datasheets%20&%20manuals/SN76489.pdf + +* John Kortink's article on the SN76489: + http://web.inter.nl.net/users/J.Kortink/home/articles/sn76489/ + +* Maxim's "SN76489 notes" in + http://www.smspower.org/maxim/docs/SN76489.txt diff --git a/quartus/sn76489-1.0/sn76489_attenuator-c.vhd b/quartus/sn76489-1.0/sn76489_attenuator-c.vhd new file mode 100644 index 0000000..b97da9d --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_attenuator-c.vhd @@ -0,0 +1,14 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_attenuator-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_attenuator_rtl_c0 of sn76489_attenuator is + + for rtl + end for; + +end sn76489_attenuator_rtl_c0; diff --git a/quartus/sn76489-1.0/sn76489_attenuator.vhd b/quartus/sn76489-1.0/sn76489_attenuator.vhd new file mode 100644 index 0000000..444064e --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_attenuator.vhd @@ -0,0 +1,114 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_attenuator.vhd,v 1.7 2006/02/27 20:30:10 arnim Exp $ +-- +-- Attenuator Module +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_attenuator is + + port ( + attenuation_i : in std_logic_vector(0 to 3); + factor_i : in signed(0 to 1); + product_o : out signed(0 to 7) + ); + +end sn76489_attenuator; + + +architecture rtl of sn76489_attenuator is + +begin + + ----------------------------------------------------------------------------- + -- Process attenuate + -- + -- Purpose: + -- Determine the attenuation and generate the resulting product. + -- + -- The maximum attenuation value is 31 which corresponds to volume off. + -- As described in the data sheet, the maximum "playing" attenuation is + -- 28 = 16 + 8 + 4 + -- + -- The table for the volume constants is derived from the following + -- formula (each step is 2dB voltage): + -- v(0) = 31 + -- v(n+1) = v(n) * 0.79432823 + -- + attenuate: process (attenuation_i, + factor_i) + + type volume_t is array (natural range 0 to 15) of natural; + constant volume_c : volume_t := + (31, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 2, 2, 1, 0); + + variable attenuation_v : unsigned(attenuation_i'range); + variable volume_v : signed(product_o'range); + + begin + + attenuation_v := unsigned(attenuation_i); + + -- volume look-up table + volume_v := to_signed(volume_c(to_integer(attenuation_v)), + product_o'length); + + -- this replaces a multiplier and consumes a bit fewer + -- resources + case to_integer(factor_i) is + when +1 => + product_o <= volume_v; + when -1 => + product_o <= -volume_v; + when others => + product_o <= (others => '0'); + end case; + + end process attenuate; + -- + ----------------------------------------------------------------------------- + +end rtl; diff --git a/quartus/sn76489-1.0/sn76489_clock_div-c.vhd b/quartus/sn76489-1.0/sn76489_clock_div-c.vhd new file mode 100644 index 0000000..7dfd851 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_clock_div-c.vhd @@ -0,0 +1,14 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_clock_div-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_clock_div_rtl_c0 of sn76489_clock_div is + + for rtl + end for; + +end sn76489_clock_div_rtl_c0; diff --git a/quartus/sn76489-1.0/sn76489_clock_div.vhd b/quartus/sn76489-1.0/sn76489_clock_div.vhd new file mode 100644 index 0000000..eab86be --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_clock_div.vhd @@ -0,0 +1,134 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_clock_div.vhd,v 1.4 2005/10/10 21:51:27 arnim Exp $ +-- +-- Clock Divider Circuit +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +entity sn76489_clock_div is + + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + clk_en_o : out boolean + ); + +end sn76489_clock_div; + + +library ieee; +use ieee.numeric_std.all; + +architecture rtl of sn76489_clock_div is + + signal cnt_s, + cnt_q : unsigned(3 downto 0); + +begin + + ----------------------------------------------------------------------------- + -- Process seq + -- + -- Purpose: + -- Implements the sequential counter element. + -- + seq: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + cnt_q <= (others => '0'); + elsif clock_i'event and clock_i = '1' then + cnt_q <= cnt_s; + end if; + end process seq; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Process comb + -- + -- Purpose: + -- Implements the combinational counter logic. + -- + comb: process (clock_en_i, + cnt_q) + begin + -- default assignments + cnt_s <= cnt_q; + clk_en_o <= false; + + if clock_en_i = '1' then + + if cnt_q = 0 then + clk_en_o <= true; + + if clock_div_16_g = 1 then + cnt_s <= to_unsigned(15, cnt_q'length); + elsif clock_div_16_g = 0 then + cnt_s <= to_unsigned( 1, cnt_q'length); + else + -- pragma translate_off + assert false + report "Generic clock_div_16_g must be either 0 or 1." + severity failure; + -- pragma translate_on + end if; + + else + cnt_s <= cnt_q - 1; + + end if; + + end if; + + end process comb; + -- + ----------------------------------------------------------------------------- + +end rtl; diff --git a/quartus/sn76489-1.0/sn76489_comp_pack-p.vhd b/quartus/sn76489-1.0/sn76489_comp_pack-p.vhd new file mode 100644 index 0000000..06b12c8 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_comp_pack-p.vhd @@ -0,0 +1,96 @@ +------------------------------------------------------------------------------- +-- +-- $Id: sn76489_comp_pack-p.vhd,v 1.6 2006/02/27 20:30:10 arnim Exp $ +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +package sn76489_comp_pack is + + component sn76489_attenuator + port ( + attenuation_i : in std_logic_vector(0 to 3); + factor_i : in signed(0 to 1); + product_o : out signed(0 to 7) + ); + end component; + + component sn76489_tone + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + ff_o : out std_logic; + tone_o : out signed(0 to 7) + ); + end component; + + component sn76489_noise + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + tone3_ff_i : in std_logic; + noise_o : out signed(0 to 7) + ); + end component; + + component sn76489_latch_ctrl + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + d_i : in std_logic_vector(0 to 7); + ready_o : out std_logic; + tone1_we_o : out boolean; + tone2_we_o : out boolean; + tone3_we_o : out boolean; + noise_we_o : out boolean; + r2_o : out std_logic + ); + end component; + + component sn76489_clock_div + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + clk_en_o : out boolean + ); + end component; + + component sn76489_top + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + ready_o : out std_logic; + d_i : in std_logic_vector(0 to 7); + aout_o : out signed(0 to 7) + ); + end component; + +end sn76489_comp_pack; diff --git a/quartus/sn76489-1.0/sn76489_latch_ctrl-c.vhd b/quartus/sn76489-1.0/sn76489_latch_ctrl-c.vhd new file mode 100644 index 0000000..abc09e0 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_latch_ctrl-c.vhd @@ -0,0 +1,14 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_latch_ctrl-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_latch_ctrl_rtl_c0 of sn76489_latch_ctrl is + + for rtl + end for; + +end sn76489_latch_ctrl_rtl_c0; diff --git a/quartus/sn76489-1.0/sn76489_latch_ctrl.vhd b/quartus/sn76489-1.0/sn76489_latch_ctrl.vhd new file mode 100644 index 0000000..789720c --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_latch_ctrl.vhd @@ -0,0 +1,138 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_latch_ctrl.vhd,v 1.6 2006/02/27 20:30:10 arnim Exp $ +-- +-- Latch Control Unit +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +entity sn76489_latch_ctrl is + + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + d_i : in std_logic_vector(0 to 7); + ready_o : out std_logic; + tone1_we_o : out boolean; + tone2_we_o : out boolean; + tone3_we_o : out boolean; + noise_we_o : out boolean; + r2_o : out std_logic + ); + +end sn76489_latch_ctrl; + + +library ieee; +use ieee.numeric_std.all; + +architecture rtl of sn76489_latch_ctrl is + + signal reg_q : std_logic_vector(0 to 2); + signal we_q : boolean; + signal ready_q : std_logic; + +begin + + ----------------------------------------------------------------------------- + -- Process seq + -- + -- Purpose: + -- Implements the sequential elements. + -- + seq: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + reg_q <= (others => '0'); + we_q <= false; + ready_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + -- READY Flag Output ---------------------------------------------------- + if ready_q = '0' and we_q then + if clk_en_i then + -- assert READY when write access happened + ready_q <= '1'; + end if; + elsif ce_n_i = '1' then + -- deassert READY when access has finished + ready_q <= '0'; + end if; + + -- Register Selection --------------------------------------------------- + if ce_n_i = '0' and we_n_i = '0' then + if clk_en_i then + if d_i(0) = '1' then + reg_q <= d_i(1 to 3); + end if; + we_q <= true; + end if; + else + we_q <= false; + end if; + + end if; + end process seq; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Output mapping + ----------------------------------------------------------------------------- + tone1_we_o <= reg_q(0 to 1) = "00" and we_q; + tone2_we_o <= reg_q(0 to 1) = "01" and we_q; + tone3_we_o <= reg_q(0 to 1) = "10" and we_q; + noise_we_o <= reg_q(0 to 1) = "11" and we_q; + + r2_o <= reg_q(2); + + ready_o <= ready_q + when ce_n_i = '0' else + '1'; + +end rtl; diff --git a/quartus/sn76489-1.0/sn76489_noise-c.vhd b/quartus/sn76489-1.0/sn76489_noise-c.vhd new file mode 100644 index 0000000..28ded84 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_noise-c.vhd @@ -0,0 +1,19 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_noise-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_noise_rtl_c0 of sn76489_noise is + + for rtl + + for attenuator_b : sn76489_attenuator + use configuration work.sn76489_attenuator_rtl_c0; + end for; + + end for; + +end sn76489_noise_rtl_c0; diff --git a/quartus/sn76489-1.0/sn76489_noise.vhd b/quartus/sn76489-1.0/sn76489_noise.vhd new file mode 100644 index 0000000..8b2ee0e --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_noise.vhd @@ -0,0 +1,281 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_noise.vhd,v 1.6 2006/02/27 20:30:10 arnim Exp $ +-- +-- Noise Generator +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_noise is + + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + tone3_ff_i : in std_logic; + noise_o : out signed(0 to 7) + ); + +end sn76489_noise; + + +use work.sn76489_comp_pack.sn76489_attenuator; + +architecture rtl of sn76489_noise is + + signal nf_q : std_logic_vector(0 to 1); + signal fb_q : std_logic; + signal a_q : std_logic_vector(0 to 3); + signal freq_cnt_q : unsigned(0 to 6); + signal freq_ff_q : std_logic; + + signal shift_source_s, + shift_source_q : std_logic; + signal shift_rise_edge_s : boolean; + + signal lfsr_q : std_logic_vector(0 to 15); + + signal freq_s : signed(0 to 1); + +begin + + ----------------------------------------------------------------------------- + -- Process cpu_regs + -- + -- Purpose: + -- Implements the registers writable by the CPU. + -- + cpu_regs: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + nf_q <= (others => '0'); + fb_q <= '0'; + a_q <= (others => '1'); + + elsif clock_i'event and clock_i = '1' then + if clk_en_i and we_i then + if r2_i = '0' then + -- access to control register + -- both access types can write to the control register! + nf_q <= d_i(6 to 7); + fb_q <= d_i(5); + + else + -- access to attenuator register + -- both access types can write to the attenuator register! + a_q <= d_i(4 to 7); + + end if; + end if; + end if; + end process cpu_regs; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Process freq_gen + -- + -- Purpose: + -- Implements the frequency generation components. + -- + freq_gen: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + freq_cnt_q <= (others => '0'); + freq_ff_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + if freq_cnt_q = 0 then + -- reload frequency counter according to NF setting + case nf_q is + when "00" => + freq_cnt_q <= to_unsigned(16 * 2 - 1, freq_cnt_q'length); + when "01" => + freq_cnt_q <= to_unsigned(16 * 4 - 1, freq_cnt_q'length); + when "10" => + freq_cnt_q <= to_unsigned(16 * 8 - 1, freq_cnt_q'length); + when others => + null; + end case; + + freq_ff_q <= not freq_ff_q; + + else + -- decrement frequency counter + freq_cnt_q <= freq_cnt_q - 1; + + end if; + + end if; + end if; + end process freq_gen; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Multiplex the source of the LFSR's shift enable + ----------------------------------------------------------------------------- + shift_source_s <= tone3_ff_i + when nf_q = "11" else + freq_ff_q; + + ----------------------------------------------------------------------------- + -- Process rise_edge + -- + -- Purpose: + -- Detect the rising edge of the selected LFSR shift source. + -- + rise_edge: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + shift_source_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + shift_source_q <= shift_source_s; + end if; + end if; + end process rise_edge; + -- + ----------------------------------------------------------------------------- + + -- detect rising edge on shift source + shift_rise_edge_s <= shift_source_q = '0' and shift_source_s = '1'; + + + ----------------------------------------------------------------------------- + -- Process lfsr + -- + -- Purpose: + -- Implements the LFSR that generates noise. + -- Note: This implementation shifts the register right, i.e. from index + -- 15 towards 0 => bit 15 is the input, bit 0 is the output + -- + -- Tapped bits according to MAME's sn76496.c, implemented in function + -- lfsr_tapped_f. + -- + lfsr: process (clock_i, res_n_i) + + function lfsr_tapped_f(lfsr : in std_logic_vector) return std_logic is + constant tapped_bits_c : std_logic_vector(0 to 15) + -- tapped bits are 0, 2, 15 + := "1010000000000001"; + variable parity_v : std_logic; + begin + parity_v := '0'; + + for idx in lfsr'low to lfsr'high loop + parity_v := parity_v xor (lfsr(idx) and tapped_bits_c(idx)); + end loop; + + return parity_v; + end; + + begin + if res_n_i = '0' then + -- reset LFSR to "0000000000000001" + lfsr_q <= (others => '0'); + lfsr_q(lfsr_q'right) <= '1'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + if we_i and r2_i = '0' then + -- write to noise register + -- -> reset LFSR + lfsr_q <= (others => '0'); + lfsr_q(lfsr_q'right) <= '1'; + + elsif shift_rise_edge_s then + + -- shift LFSR left towards MSB + for idx in lfsr_q'right-1 downto lfsr_q'left loop + lfsr_q(idx) <= lfsr_q(idx+1); + end loop; + + -- determine input bit + if fb_q = '0' then + -- "Periodic" Noise + -- -> input to LFSR is output + lfsr_q(lfsr_q'right) <= lfsr_q(lfsr_q'left); + else + -- "White" Noise + -- -> input to LFSR is parity of tapped bits + lfsr_q(lfsr_q'right) <= lfsr_tapped_f(lfsr_q); + end if; + + end if; + + end if; + end if; + end process lfsr; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Map output of LFSR to signed value for attenuator. + ----------------------------------------------------------------------------- + freq_s <= to_signed(+1, 2) + when lfsr_q(0) = '1' else + to_signed( 0, 2); + + + ----------------------------------------------------------------------------- + -- The attenuator itself + ----------------------------------------------------------------------------- + attenuator_b : sn76489_attenuator + port map ( + attenuation_i => a_q, + factor_i => freq_s, + product_o => noise_o + ); + +end rtl; diff --git a/quartus/sn76489-1.0/sn76489_tone-c.vhd b/quartus/sn76489-1.0/sn76489_tone-c.vhd new file mode 100644 index 0000000..119e2f3 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_tone-c.vhd @@ -0,0 +1,19 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_tone-c.vhd,v 1.2 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_tone_rtl_c0 of sn76489_tone is + + for rtl + + for attenuator_b : sn76489_attenuator + use configuration work.sn76489_attenuator_rtl_c0; + end for; + + end for; + +end sn76489_tone_rtl_c0; diff --git a/quartus/sn76489-1.0/sn76489_tone.vhd b/quartus/sn76489-1.0/sn76489_tone.vhd new file mode 100644 index 0000000..b71af8b --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_tone.vhd @@ -0,0 +1,191 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_tone.vhd,v 1.5 2006/02/27 20:30:10 arnim Exp $ +-- +-- Tone Generator +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_tone is + + port ( + clock_i : in std_logic; + clk_en_i : in boolean; + res_n_i : in std_logic; + we_i : in boolean; + d_i : in std_logic_vector(0 to 7); + r2_i : in std_logic; + ff_o : out std_logic; + tone_o : out signed(0 to 7) + ); + +end sn76489_tone; + + +use work.sn76489_comp_pack.sn76489_attenuator; + +architecture rtl of sn76489_tone is + + signal f_q : std_logic_vector(0 to 9); + signal a_q : std_logic_vector(0 to 3); + signal freq_cnt_q : unsigned(0 to 9); + signal freq_ff_q : std_logic; + + signal freq_s : signed(0 to 1); + + function all_zero(a : in std_logic_vector) return boolean is + variable result_v : boolean; + begin + result_v := true; + + for idx in a'low to a'high loop + if a(idx) /= '0' then + result_v := false; + end if; + end loop; + + return result_v; + end; + +begin + + ----------------------------------------------------------------------------- + -- Process cpu_regs + -- + -- Purpose: + -- Implements the registers writable by the CPU. + -- + cpu_regs: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + f_q <= (others => '0'); + a_q <= (others => '1'); + + elsif clock_i'event and clock_i = '1' then + if clk_en_i and we_i then + if r2_i = '0' then + -- access to frequency register + if d_i(0) = '0' then + f_q(0 to 5) <= d_i(2 to 7); + else + f_q(6 to 9) <= d_i(4 to 7); + end if; + + else + -- access to attenuator register + -- both access types can write to the attenuator register! + a_q <= d_i(4 to 7); + + end if; + end if; + end if; + end process cpu_regs; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Process freq_gen + -- + -- Purpose: + -- Implements the frequency generation components. + -- + freq_gen: process (clock_i, res_n_i) + begin + if res_n_i = '0' then + freq_cnt_q <= (others => '0'); + freq_ff_q <= '0'; + + elsif clock_i'event and clock_i = '1' then + if clk_en_i then + if freq_cnt_q = 0 then + -- update counter from frequency register + freq_cnt_q <= unsigned(f_q); + + -- and toggle the frequency flip-flop if enabled + if not all_zero(f_q) then + freq_ff_q <= not freq_ff_q; + else + -- if frequency setting is 0, then keep flip-flop at +1 + freq_ff_q <= '1'; + end if; + + else + -- decrement frequency counter + freq_cnt_q <= freq_cnt_q - 1; + + end if; + end if; + end if; + end process freq_gen; + -- + ----------------------------------------------------------------------------- + + + ----------------------------------------------------------------------------- + -- Map frequency flip-flop to signed value for attenuator. + ----------------------------------------------------------------------------- + freq_s <= to_signed(+1, 2) + when freq_ff_q = '1' else + to_signed(-1, 2); + + + ----------------------------------------------------------------------------- + -- The attenuator itself + ----------------------------------------------------------------------------- + attenuator_b : sn76489_attenuator + port map ( + attenuation_i => a_q, + factor_i => freq_s, + product_o => tone_o + ); + + + ----------------------------------------------------------------------------- + -- Output mapping + ----------------------------------------------------------------------------- + ff_o <= freq_ff_q; + +end rtl; diff --git a/quartus/sn76489-1.0/sn76489_top-c.vhd b/quartus/sn76489-1.0/sn76489_top-c.vhd new file mode 100644 index 0000000..8f709b9 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_top-c.vhd @@ -0,0 +1,31 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_top-c.vhd,v 1.3 2005/10/10 22:12:38 arnim Exp $ +-- +------------------------------------------------------------------------------- + +configuration sn76489_top_struct_c0 of sn76489_top is + + for struct + + for clock_div_b : sn76489_clock_div + use configuration work.sn76489_clock_div_rtl_c0; + end for; + + for latch_ctrl_b : sn76489_latch_ctrl + use configuration work.sn76489_latch_ctrl_rtl_c0; + end for; + + for all : sn76489_tone + use configuration work.sn76489_tone_rtl_c0; + end for; + + for noise_b : sn76489_noise + use configuration work.sn76489_noise_rtl_c0; + end for; + + end for; + +end sn76489_top_struct_c0; diff --git a/quartus/sn76489-1.0/sn76489_top.vhd b/quartus/sn76489-1.0/sn76489_top.vhd new file mode 100644 index 0000000..277f601 --- /dev/null +++ b/quartus/sn76489-1.0/sn76489_top.vhd @@ -0,0 +1,210 @@ +------------------------------------------------------------------------------- +-- +-- Synthesizable model of TI's SN76489AN. +-- +-- $Id: sn76489_top.vhd,v 1.9 2006/02/27 20:30:10 arnim Exp $ +-- +-- Chip Toplevel +-- +-- References: +-- +-- * TI Data sheet SN76489.pdf +-- ftp://ftp.whtech.com/datasheets%20&%20manuals/SN76489.pdf +-- +-- * John Kortink's article on the SN76489: +-- http://web.inter.nl.net/users/J.Kortink/home/articles/sn76489/ +-- +-- * Maxim's "SN76489 notes" in +-- http://www.smspower.org/maxim/docs/SN76489.txt +-- +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2005, 2006, Arnim Laeuger (arnim.laeuger@gmx.net) +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- Please report bugs to the author, but before you do so, please +-- make sure that this is not a derivative work and that +-- you have the latest version of this file. +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity sn76489_top is + + generic ( + clock_div_16_g : integer := 1 + ); + port ( + clock_i : in std_logic; + clock_en_i : in std_logic; + res_n_i : in std_logic; + ce_n_i : in std_logic; + we_n_i : in std_logic; + ready_o : out std_logic; + d_i : in std_logic_vector(0 to 7); + aout_o : out signed(0 to 7) + ); + +end sn76489_top; + + +library ieee; +use ieee.numeric_std.all; +use work.sn76489_comp_pack.all; + +architecture struct of sn76489_top is + + signal clk_en_s : boolean; + + signal tone1_we_s, + tone2_we_s, + tone3_we_s, + noise_we_s : boolean; + signal r2_s : std_logic; + + signal tone1_s, + tone2_s, + tone3_s, + noise_s : signed(0 to 7); + + signal tone3_ff_s : std_logic; + +begin + + ----------------------------------------------------------------------------- + -- Clock Divider + ----------------------------------------------------------------------------- + clock_div_b : sn76489_clock_div + generic map ( + clock_div_16_g => clock_div_16_g + ) + port map ( + clock_i => clock_i, + clock_en_i => clock_en_i, + res_n_i => res_n_i, + clk_en_o => clk_en_s + ); + + + ----------------------------------------------------------------------------- + -- Latch Control = CPU Interface + ----------------------------------------------------------------------------- + latch_ctrl_b : sn76489_latch_ctrl + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + ce_n_i => ce_n_i, + we_n_i => we_n_i, + d_i => d_i, + ready_o => ready_o, + tone1_we_o => tone1_we_s, + tone2_we_o => tone2_we_s, + tone3_we_o => tone3_we_s, + noise_we_o => noise_we_s, + r2_o => r2_s + ); + + + ----------------------------------------------------------------------------- + -- Tone Channel 1 + ----------------------------------------------------------------------------- + tone1_b : sn76489_tone + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => tone1_we_s, + d_i => d_i, + r2_i => r2_s, + ff_o => open, + tone_o => tone1_s + ); + + ----------------------------------------------------------------------------- + -- Tone Channel 2 + ----------------------------------------------------------------------------- + tone2_b : sn76489_tone + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => tone2_we_s, + d_i => d_i, + r2_i => r2_s, + ff_o => open, + tone_o => tone2_s + ); + + ----------------------------------------------------------------------------- + -- Tone Channel 3 + ----------------------------------------------------------------------------- + tone3_b : sn76489_tone + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => tone3_we_s, + d_i => d_i, + r2_i => r2_s, + ff_o => tone3_ff_s, + tone_o => tone3_s + ); + + ----------------------------------------------------------------------------- + -- Noise Channel + ----------------------------------------------------------------------------- + noise_b : sn76489_noise + port map ( + clock_i => clock_i, + clk_en_i => clk_en_s, + res_n_i => res_n_i, + we_i => noise_we_s, + d_i => d_i, + r2_i => r2_s, + tone3_ff_i => tone3_ff_s, + noise_o => noise_s + ); + + + -- Register output + process(clock_i) + begin + if res_n_i = '0' then + aout_o <= (others => '0'); + elsif rising_edge(clock_i) then + aout_o <= tone1_s + tone2_s + tone3_s + noise_s; + end if; + end process; + +end struct; diff --git a/quartus/vidproc.vhd b/quartus/vidproc.vhd new file mode 100644 index 0000000..6d5f91f --- /dev/null +++ b/quartus/vidproc.vhd @@ -0,0 +1,279 @@ +-- BBC Micro for Altera DE1 +-- +-- Copyright (c) 2011 Mike Stirling +-- +-- All rights reserved +-- +-- Redistribution and use in source and synthezised forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- +-- * Redistributions in synthesized form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- * Neither the name of the author nor the names of other contributors may +-- be used to endorse or promote products derived from this software without +-- specific prior written agreement from the author. +-- +-- * License is granted for non-commercial use only. A fee may not be charged +-- for redistributions as source code or in synthesized/hardware form without +-- specific prior written agreement from the author. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +-- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +-- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +-- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +-- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +-- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +-- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +-- POSSIBILITY OF SUCH DAMAGE. +-- +-- BBC Micro "VIDPROC" Video ULA +-- +-- Synchronous implementation for FPGA +-- +-- (C) 2011 Mike Stirling +-- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity vidproc is +port ( + CLOCK : in std_logic; + -- Clock enable qualifies display cycles (interleaved with CPU cycles) + CLKEN : in std_logic; + nRESET : in std_logic; + + -- Clock enable output to CRTC + CLKEN_CRTC : out std_logic; + + -- Bus interface + ENABLE : in std_logic; + A0 : in std_logic; + -- CPU data bus (for register writes) + DI_CPU : in std_logic_vector(7 downto 0); + -- Display RAM data bus (for display data fetch) + DI_RAM : in std_logic_vector(7 downto 0); + + -- Control interface + nINVERT : in std_logic; + DISEN : in std_logic; + CURSOR : in std_logic; + + -- Video in (teletext mode) + R_IN : in std_logic; + G_IN : in std_logic; + B_IN : in std_logic; + + -- Video out + R : out std_logic; + G : out std_logic; + B : out std_logic + ); +end entity; + +architecture rtl of vidproc is +-- Write-only registers +signal r0_cursor0 : std_logic; +signal r0_cursor1 : std_logic; +signal r0_cursor2 : std_logic; +signal r0_crtc_2mhz : std_logic; +signal r0_pixel_rate : std_logic_vector(1 downto 0); +signal r0_teletext : std_logic; +signal r0_flash : std_logic; + +type palette_t is array(0 to 15) of std_logic_vector(3 downto 0); +signal palette : palette_t; + +-- Pixel shift register +signal shiftreg : std_logic_vector(7 downto 0); +-- Delayed display enable +signal delayed_disen : std_logic; + +-- Internal clock enable generation +signal clken_pixel : std_logic; +signal clken_fetch : std_logic; +signal clken_counter : unsigned(3 downto 0); + +-- Cursor generation - can span up to 32 pixels +-- Segments 0 and 1 are 8 pixels wide +-- Segment 2 is 16 pixels wide +signal cursor_invert : std_logic; +signal cursor_active : std_logic; +signal cursor_counter : unsigned(1 downto 0); + +begin + -- Synchronous register access, enabled on every clock + process(CLOCK,nRESET) + begin + if nRESET = '0' then + r0_cursor0 <= '0'; + r0_cursor1 <= '0'; + r0_cursor2 <= '0'; + r0_crtc_2mhz <= '0'; + r0_pixel_rate <= "00"; + r0_teletext <= '0'; + r0_flash <= '0'; + + for colour in 0 to 15 loop + palette(colour) <= (others => '0'); + end loop; + elsif rising_edge(CLOCK) then + if ENABLE = '1' then + if A0 = '0' then + -- Access control register + r0_cursor0 <= DI_CPU(7); + r0_cursor1 <= DI_CPU(6); + r0_cursor2 <= DI_CPU(5); + r0_crtc_2mhz <= DI_CPU(4); + r0_pixel_rate <= DI_CPU(3 downto 2); + r0_teletext <= DI_CPU(1); + r0_flash <= DI_CPU(0); + else + -- Access palette register + palette(to_integer(unsigned(DI_CPU(7 downto 4)))) <= DI_CPU(3 downto 0); + end if; + end if; + end if; + end process; + + -- Clock enable generation. + -- Pixel clock can be divided by 1,2,4 or 8 depending on the value + -- programmed at r0_pixel_rate + -- 00 = /8, 01 = /4, 10 = /2, 11 = /1 + clken_pixel <= + CLKEN when r0_pixel_rate = "11" else + (CLKEN and not clken_counter(0)) when r0_pixel_rate = "10" else + (CLKEN and not (clken_counter(0) or clken_counter(1))) when r0_pixel_rate = "01" else + (CLKEN and not (clken_counter(0) or clken_counter(1) or clken_counter(2))); + -- The CRT controller is always enabled in the 15th cycle, so that the result + -- is ready for latching into the shift register in cycle 0. If 2 MHz mode is + -- selected then the CRTC is also enabled in the 7th cycle + CLKEN_CRTC <= CLKEN and + clken_counter(0) and clken_counter(1) and clken_counter(2) and + (clken_counter(3) or r0_crtc_2mhz); + -- The result is fetched from the CRTC in cycle 0 and also cycle 8 if 2 MHz + -- mode is selected. This is used for reloading the shift register as well as + -- counting cursor pixels + clken_fetch <= CLKEN and not (clken_counter(0) or clken_counter(1) or clken_counter(2) or + (clken_counter(3) and not r0_crtc_2mhz)); + + process(CLOCK,nRESET) + begin + if nRESET = '0' then + clken_counter <= (others => '0'); + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Increment internal cycle counter during each video clock + clken_counter <= clken_counter + 1; + end if; + end process; + + -- Fetch control + process(CLOCK,nRESET) + begin + if nRESET = '0' then + shiftreg <= (others => '0'); + elsif rising_edge(CLOCK) and clken_pixel = '1' then + if clken_fetch = '1' then + -- Fetch next byte from RAM into shift register. This always occurs in + -- cycle 0, and also in cycle 8 if the CRTC is clocked at double rate. + shiftreg <= DI_RAM; + else + -- Clock shift register and input '1' at LSB + shiftreg <= shiftreg(6 downto 0) & "1"; + end if; + end if; + end process; + + -- Cursor generation + cursor_invert <= cursor_active and + ((r0_cursor0 and not (cursor_counter(0) or cursor_counter(1))) or + (r0_cursor1 and cursor_counter(0) and not cursor_counter(1)) or + (r0_cursor2 and cursor_counter(1))); + process(CLOCK,nRESET) + begin + if nRESET = '0' then + cursor_active <= '0'; + cursor_counter <= (others => '0'); + elsif rising_edge(CLOCK) and clken_fetch = '1' then + if CURSOR = '1' or cursor_active = '1' then + -- Latch cursor + cursor_active <= '1'; + + -- Reset on counter wrap + if cursor_counter = "11" then + cursor_active <= '0'; + end if; + + -- Increment counter + if cursor_active = '0' then + -- Reset + cursor_counter <= (others => '0'); + else + -- Increment + cursor_counter <= cursor_counter + 1; + end if; + end if; + end if; + end process; + + -- Pixel generation + -- The new shift register contents are loaded during + -- cycle 0 (and 8) but will not be read here until the next cycle. + -- By running this process on every single video tick instead of at + -- the pixel rate we ensure that the resulting delay is minimal and + -- constant (running this at the pixel rate would cause + -- the display to move slightly depending on which mode was selected). + process(CLOCK,nRESET) + variable palette_a : std_logic_vector(3 downto 0); + variable dot_val : std_logic_vector(3 downto 0); + variable red_val : std_logic; + variable green_val : std_logic; + variable blue_val : std_logic; + begin + if nRESET = '0' then + R <= '0'; + G <= '0'; + B <= '0'; + delayed_disen <= '0'; + elsif rising_edge(CLOCK) and CLKEN = '1' then + -- Look up dot value in the palette. Bits are as follows: + -- bit 3 - FLASH + -- bit 2 - Not BLUE + -- bit 1 - Not GREEN + -- bit 0 - Not RED + palette_a := shiftreg(7) & shiftreg(5) & shiftreg(3) & shiftreg(1); + dot_val := palette(to_integer(unsigned(palette_a))); + + -- Apply flash inversion if required + red_val := (dot_val(3) and r0_flash) xor not dot_val(0); + green_val := (dot_val(3) and r0_flash) xor not dot_val(1); + blue_val := (dot_val(3) and r0_flash) xor not dot_val(2); + + -- To output + -- FIXME: INVERT option + if r0_teletext = '0' then + -- Cursor can extend outside the bounds of the screen, so + -- it is not affected by DISEN + R <= (red_val and delayed_disen) xor cursor_invert; + G <= (green_val and delayed_disen) xor cursor_invert; + B <= (blue_val and delayed_disen) xor cursor_invert; + else + R <= R_IN xor cursor_invert; + G <= G_IN xor cursor_invert; + B <= B_IN xor cursor_invert; + end if; + + -- Display enable signal delayed by one clock + delayed_disen <= DISEN; + end if; + end process; +end architecture; + -- cgit v1.2.3