diff --git a/.gitignore b/.gitignore index eab3b43..495f775 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ .build .Xil -hdmi.sdk diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5508e2f --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +BUILD = .build + +VERILOG = \ +src/char_map.v \ +src/color_map.v \ +src/dvi_top.v \ +src/dvid.v \ +src/encode.v \ +src/hdmi.xdc \ +src/simple_dual_ram.v \ + +.PHONY: all clean + +all: $(BUILD)/impl.bin + +$(BUILD)/ip/clk_wiz_0/clk_wiz_0.xci: scripts/clk_wiz_0.tcl | $(BUILD)/ip + /opt/Xilinx/Vivado/2019.1/bin/vivado -mode batch -source scripts/clk_wiz_0.tcl -nolog -nojournal + +$(BUILD)/ip/microblaze_mcs_0/microblaze_mcs_0.xci: scripts/microblaze_mcs_0.tcl | $(BUILD)/ip + /opt/Xilinx/Vivado/2019.1/bin/vivado -mode batch -source scripts/microblaze_mcs_0.tcl -nolog -nojournal + +$(BUILD)/5_route.dcp: $(VERILOG) $(BUILD)/ip/clk_wiz_0/clk_wiz_0.xci $(BUILD)/ip/microblaze_mcs_0/microblaze_mcs_0.xci scripts/build.tcl | $(BUILD) + /opt/Xilinx/Vivado/2019.1/bin/vivado -mode batch -source scripts/build.tcl -nolog -nojournal + +$(BUILD)/impl.bin: $(BUILD)/5_route.dcp $(BUILD)/controller.elf scripts/write.tcl | $(BUILD) + /opt/Xilinx/Vivado/2019.1/bin/vivado -mode batch -source scripts/write.tcl -nolog -nojournal + +$(BUILD)/bsp/system.mss: $(BUILD)/system.hdf | $(BUILD) + /opt/Xilinx/SDK/2019.1/bin/xsdk -batch scripts/sdk.tcl + +$(BUILD)/bsp/microblaze_I/lib/libxil.a: $(BUILD)/bsp/system.mss + $(MAKE) -C $(BUILD)/bsp all + +OBJ = \ +$(BUILD)/obj/main.o + +$(BUILD)/obj/%.o: software/src/%.c $(BUILD)/bsp/microblaze_I/lib/libxil.a | $(BUILD)/obj + mb-gcc -Wall -O2 -c -fmessage-length=0 -MT"$@" -I$(BUILD)/bsp/microblaze_I/include -mno-xl-reorder -mlittle-endian -mcpu=v11.0 -mxl-soft-mul -Wl,--no-relax -ffunction-sections -fdata-sections -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" + +$(BUILD)/controller.elf: $(OBJ) software/src/lscript.ld $(BUILD)/bsp/microblaze_I/lib/libxil.a | $(BUILD) + mb-gcc -Wl,-T -Wl,software/src/lscript.ld -L$(BUILD)/bsp/microblaze_I/lib -mlittle-endian -mcpu=v11.0 -mxl-soft-mul -Wl,--no-relax -Wl,--gc-sections -o "$@" $(OBJ) -Wl,--start-group,-lxil,-lgcc,-lc,--end-group + +$(BUILD): + mkdir $@ + +$(BUILD)/ip: | $(BUILD) + mkdir $@ + +$(BUILD)/obj: | $(BUILD) + mkdir $@ + +clean: + rm -fr $(BUILD) diff --git a/controller.elf b/controller.elf deleted file mode 100755 index 8fab699..0000000 Binary files a/controller.elf and /dev/null differ diff --git a/build.tcl b/scripts/build.tcl similarity index 94% rename from build.tcl rename to scripts/build.tcl index fad523d..9b2b0f5 100644 --- a/build.tcl +++ b/scripts/build.tcl @@ -10,17 +10,17 @@ foreach fname [glob -nocomplain -type f $out_dir/*] { } set step 0_ip -if { [file isdirectory ./.build/ip] } { - read_ip ./.build/ip/clk_wiz_0/clk_wiz_0.xci - read_ip ./.build/ip/microblaze_mcs_0/microblaze_mcs_0.xci +if { [file isdirectory ${out_dir}/ip] } { + read_ip ${out_dir}/ip/clk_wiz_0/clk_wiz_0.xci + read_ip ${out_dir}/ip/microblaze_mcs_0/microblaze_mcs_0.xci } else { - file mkdir ./.build/ip + file mkdir ${out_dir}/ip create_project -in_memory create_ip -name clk_wiz -vendor xilinx.com -library ip \ -version 6.0 -module_name clk_wiz_0 \ - -dir ./.build/ip + -dir ${out_dir}/ip set_property -dict [list \ CONFIG.PRIMARY_PORT {clk_in} \ @@ -58,7 +58,7 @@ if { [file isdirectory ./.build/ip] } { create_ip -name microblaze_mcs -vendor xilinx.com -library ip \ -version 3.0 -module_name microblaze_mcs_0 \ - -dir ./.build/ip + -dir ${out_dir}/ip set_property -dict [list \ CONFIG.FREQ {150} \ @@ -134,4 +134,4 @@ report_drc -file $out_dir/${step}_drc.rpt write_verilog -force $out_dir/imp_netlist.v write_xdc -no_fixed_only -force $out_dir/imp.xdc -source write.tcl +write_hwdef -force -file ${out_dir}/system.hdf diff --git a/scripts/clk_wiz_0.tcl b/scripts/clk_wiz_0.tcl new file mode 100644 index 0000000..abe8250 --- /dev/null +++ b/scripts/clk_wiz_0.tcl @@ -0,0 +1,50 @@ +set out_dir ./.build +set part_num xc7a35tftg256-1 + +set_part $part_num + +file delete -force $out_dir/ip/clk_wiz_0 + +create_project -in_memory + +create_ip -name clk_wiz -vendor xilinx.com -library ip \ + -version 6.0 -module_name clk_wiz_0 \ + -dir ${out_dir}/ip + +set_property -dict [list \ + CONFIG.PRIMARY_PORT {clk_in} \ + CONFIG.CLKOUT2_USED {true} \ + CONFIG.CLKOUT3_USED {true} \ + CONFIG.CLKOUT4_USED {true} \ + CONFIG.CLK_OUT1_PORT {clk_dvi} \ + CONFIG.CLK_OUT2_PORT {clk_dvin} \ + CONFIG.CLK_OUT3_PORT {clk_pix} \ + CONFIG.CLK_OUT4_PORT {clk_mcu} \ + CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {375} \ + CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {375} \ + CONFIG.CLKOUT2_REQUESTED_PHASE {180} \ + CONFIG.CLKOUT3_REQUESTED_OUT_FREQ {75} \ + CONFIG.CLKOUT4_REQUESTED_OUT_FREQ {150} \ + CONFIG.CLKOUT1_DRIVES {BUFG} \ + CONFIG.FEEDBACK_SOURCE {FDBK_AUTO} \ + CONFIG.MMCM_DIVCLK_DIVIDE {1} \ + CONFIG.MMCM_CLKFBOUT_MULT_F {7.500} \ + CONFIG.MMCM_CLKOUT0_DIVIDE_F {2.000} \ + CONFIG.MMCM_CLKOUT1_DIVIDE {2} \ + CONFIG.MMCM_CLKOUT1_PHASE {180.000} \ + CONFIG.MMCM_CLKOUT2_DIVIDE {10} \ + CONFIG.MMCM_CLKOUT3_DIVIDE {5} \ + CONFIG.NUM_OUT_CLKS {4} \ + CONFIG.CLKOUT1_JITTER {111.604} \ + CONFIG.CLKOUT1_PHASE_ERROR {116.405} \ + CONFIG.CLKOUT2_JITTER {111.604} \ + CONFIG.CLKOUT2_PHASE_ERROR {116.405} \ + CONFIG.CLKOUT3_JITTER {152.549} \ + CONFIG.CLKOUT3_PHASE_ERROR {116.405} \ + CONFIG.CLKOUT4_JITTER {132.464} \ + CONFIG.CLKOUT4_PHASE_ERROR {116.405} \ +] [get_ips clk_wiz_0] + +generate_target all [get_ips] + +synth_ip [get_ips] > $out_dir/clk_wiz_0.log diff --git a/scripts/microblaze_mcs_0.tcl b/scripts/microblaze_mcs_0.tcl new file mode 100644 index 0000000..937edc2 --- /dev/null +++ b/scripts/microblaze_mcs_0.tcl @@ -0,0 +1,36 @@ +set out_dir ./.build +set part_num xc7a35tftg256-1 + +set_part $part_num + +file delete -force $out_dir/ip/microblaze_mcs_0 + +create_project -in_memory + +create_ip -name microblaze_mcs -vendor xilinx.com -library ip \ + -version 3.0 -module_name microblaze_mcs_0 \ + -dir ${out_dir}/ip + +set_property -dict [list \ + CONFIG.FREQ {150} \ + CONFIG.MEMSIZE {131072} \ + CONFIG.USE_GPO1 {1} \ + CONFIG.GPO1_SIZE {16} \ + CONFIG.USE_GPO2 {1} \ + CONFIG.GPO2_SIZE {16} \ + CONFIG.USE_GPO3 {1} \ + CONFIG.GPO3_SIZE {2} \ + CONFIG.USE_GPO4 {1} \ + CONFIG.GPO4_SIZE {6} \ + CONFIG.USE_GPI1 {1} \ + CONFIG.GPI1_SIZE {8} \ + CONFIG.USE_GPI2 {1} \ + CONFIG.GPI2_SIZE {1} \ + CONFIG.GPI2_INTERRUPT {3} \ + CONFIG.USE_GPI3 {1} \ + CONFIG.GPI3_SIZE {1} \ +] [get_ips microblaze_mcs_0] + +generate_target all [get_ips] + +synth_ip [get_ips] > $out_dir/microblaze_mcs_0.log diff --git a/scripts/sdk.tcl b/scripts/sdk.tcl new file mode 100644 index 0000000..0389775 --- /dev/null +++ b/scripts/sdk.tcl @@ -0,0 +1,9 @@ +set out_dir ./.build + +file delete -force ${out_dir}/.metadata +file delete -force ${out_dir}/hw_platform +file delete -force ${out_dir}/bsp + +setws .build +createhw -name hw_platform -hwspec .build/system.hdf +createbsp -name bsp -hwproject hw_platform -os standalone -proc microblaze_I diff --git a/write.tcl b/scripts/write.tcl similarity index 100% rename from write.tcl rename to scripts/write.tcl diff --git a/software/src/lscript.ld b/software/src/lscript.ld new file mode 100644 index 0000000..7f7d4ee --- /dev/null +++ b/software/src/lscript.ld @@ -0,0 +1,212 @@ +/*******************************************************************/ +/* */ +/* This file is automatically generated by linker script generator.*/ +/* */ +/* Version: 2019.1 */ +/* */ +/* Copyright (c) 2010-2016 Xilinx, Inc. All rights reserved. */ +/* */ +/* Description : MicroBlaze Linker Script */ +/* */ +/*******************************************************************/ + +_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x400; +_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x800; + +/* Define Memories in the system */ + +MEMORY +{ + ilmb_cntlr_Mem_dlmb_cntlr_Mem : ORIGIN = 0x50, LENGTH = 0x7FB0 +} + +/* Specify the default entry point to the program */ + +ENTRY(_start) + +/* Define the sections, and where they are mapped in memory */ + +SECTIONS +{ +.vectors.reset 0x0 : { + KEEP (*(.vectors.reset)) +} + +.vectors.sw_exception 0x8 : { + KEEP (*(.vectors.sw_exception)) +} + +.vectors.interrupt 0x10 : { + KEEP (*(.vectors.interrupt)) +} + +.vectors.hw_exception 0x20 : { + KEEP (*(.vectors.hw_exception)) +} + +.text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.init : { + KEEP (*(.init)) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.fini : { + KEEP (*(.fini)) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.ctors : { + __CTOR_LIST__ = .; + ___CTORS_LIST___ = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __CTOR_END__ = .; + ___CTORS_END___ = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.dtors : { + __DTOR_LIST__ = .; + ___DTORS_LIST___ = .; + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + PROVIDE(__DTOR_END__ = .); + PROVIDE(___DTORS_END___ = .); +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.rodata : { + __rodata_start = .; + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + __rodata_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.sdata2 : { + . = ALIGN(8); + __sdata2_start = .; + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + . = ALIGN(8); + __sdata2_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.sbss2 : { + __sbss2_start = .; + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + __sbss2_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.data : { + . = ALIGN(4); + __data_start = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + __data_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.got : { + *(.got) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.got1 : { + *(.got1) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.got2 : { + *(.got2) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.eh_frame : { + *(.eh_frame) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.jcr : { + *(.jcr) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.gcc_except_table : { + *(.gcc_except_table) +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.sdata : { + . = ALIGN(8); + __sdata_start = .; + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + __sdata_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.sbss (NOLOAD) : { + . = ALIGN(4); + __sbss_start = .; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + . = ALIGN(8); + __sbss_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.tdata : { + __tdata_start = .; + *(.tdata) + *(.tdata.*) + *(.gnu.linkonce.td.*) + __tdata_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.tbss : { + __tbss_start = .; + *(.tbss) + *(.tbss.*) + *(.gnu.linkonce.tb.*) + __tbss_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.bss (NOLOAD) : { + . = ALIGN(4); + __bss_start = .; + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 ); + +_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 ); + +/* Generate Stack and Heap definitions */ + +.heap (NOLOAD) : { + . = ALIGN(8); + _heap = .; + _heap_start = .; + . += _HEAP_SIZE; + _heap_end = .; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +.stack (NOLOAD) : { + _stack_end = .; + . += _STACK_SIZE; + . = ALIGN(8); + _stack = .; + __stack = _stack; +} > ilmb_cntlr_Mem_dlmb_cntlr_Mem + +_end = .; +} + diff --git a/software/src/main.c b/software/src/main.c new file mode 100644 index 0000000..146718d --- /dev/null +++ b/software/src/main.c @@ -0,0 +1,268 @@ +#include +#include +#include + +XIOModule gpo; + +int8_t x = 0; +int8_t y = 0; +int8_t scroll = 0; + +uint8_t reversed = 0; +uint8_t foreground = 0b111; +uint8_t background = 0; + +uint16_t properties = 0; + +void commit() { + XIOModule_DiscreteSet(&gpo, 3, 1); + XIOModule_DiscreteClear(&gpo, 3, 1); +} + +void calculate_properties() { + if (reversed) { + properties = ((foreground << 4) + background) << 8; + } else { + properties = ((background << 4) + foreground) << 8; + } +} + +void set_foreground(uint8_t color) { + foreground = color; + + calculate_properties(); +} + +void set_background(uint8_t color) { + background = color; + + calculate_properties(); +} + +void reverse(uint8_t enable) { + reversed = enable; + + calculate_properties(); +} + +void clear_screen() { + reverse(0); + set_foreground(0b111); + set_background(0); + XIOModule_DiscreteWrite(&gpo, 1, 0); + XIOModule_DiscreteWrite(&gpo, 2, 0); + XIOModule_DiscreteSet(&gpo, 3, 1); + + for (int y = 0; y < (45+2); ++y) { + int16_t temp = (y << 8); + for (int x = 0; x < (80+24); ++x) { + XIOModule_DiscreteWrite(&gpo, 2, temp + x); + } + } + + XIOModule_DiscreteClear(&gpo, 3, 1); + + x = 0; + y = 0; + scroll = 0; + XIOModule_DiscreteWrite(&gpo, 2, (y << 8) + x); + XIOModule_DiscreteWrite(&gpo, 4, scroll); +} + +void clear_eol() { + reverse(0); + set_foreground(0b111); + set_background(0); + + int16_t temp = (y << 8); + + XIOModule_DiscreteWrite(&gpo, 1, 0); + XIOModule_DiscreteWrite(&gpo, 2, temp + x); + XIOModule_DiscreteSet(&gpo, 3, 1); + + for (int i = x; i < (80+24); ++i) { + XIOModule_DiscreteWrite(&gpo, 2, temp + i); + } + + XIOModule_DiscreteClear(&gpo, 3, 1); +} + +void write(uint8_t c) { + XIOModule_DiscreteWrite(&gpo, 1, properties + c); + XIOModule_DiscreteWrite(&gpo, 2, (y << 8) + x); + commit(); +} + +void next() { + x++; + if (x >= 80) { + y++; + x %= 80; + } + + if (y >= 45) { + y--; + scroll = (scroll + 1) % 45; + XIOModule_DiscreteWrite(&gpo, 4, scroll); + clear_eol(); + } +} + +// @todo This does not appear to work quite correctly +void previous() { + x--; + if (x < 0) { + y--; + x %= 80; + } + + if (y < 0) { + y = 0; + x = 0; + } +} + +uint8_t data = 0; +uint8_t had = 0; +uint8_t escape = 0; +uint8_t escape_parameter_1 = 0; +uint8_t escape_parameter_2 = 0; + +void clock() { + uint8_t io_write = XIOModule_DiscreteRead(&gpo, 3); + if (io_write && !had) { + XIOModule_DiscreteClear(&gpo, 3, 2); + had = 1; + data = XIOModule_DiscreteRead(&gpo, 1) & 0xFF; + + if (escape == 1) { + if (data == '[') { + escape = 2; + } else { + escape = 0; + } + } else if (escape) { + switch (data) { + // For now we are only going to implement what we actually use + case 'K': + // Assume parameter 0 + clear_eol(); + escape = 0; + break; + + case 'H': + if (escape_parameter_1 == 0) { + escape_parameter_1 = 1; + } + + if (escape_parameter_2 == 0) { + escape_parameter_2 = 1; + } + + x = escape_parameter_1 - 1; + y = escape_parameter_2 - 1; + escape = 0; + break; + + case 'm': + if (escape_parameter_1 == 0) { + reverse(0); + set_foreground(0b111); + set_background(0); + } else if (escape_parameter_1 == 7) { + reverse(1); + } else if (escape_parameter_1 >= 30 && escape_parameter_1 <= 37) { + set_foreground(escape_parameter_1 - 30); + } else if (escape_parameter_1 >= 40 && escape_parameter_1 <= 47) { + set_background(escape_parameter_1 - 40); + } else if (escape_parameter_1 >= 90 && escape_parameter_1 <= 97) { + set_foreground(escape_parameter_1 - 90 + 8); + } else if (escape_parameter_1 >= 100 && escape_parameter_1 <= 107) { + set_background(escape_parameter_1 - 100 + 8); + } + escape = 0; + break; + + case 'J': + // Assume parameter 2 + clear_screen(); + escape = 0; + break; + + case '0' ... '9': + escape_parameter_1 *= 10; + escape_parameter_1 += (data - 48); + break; + + case ';': + escape_parameter_2 = escape_parameter_1; + escape_parameter_1 = 0; + break; + + default: + escape = 0; + break; + } + } else { + switch (data) { + case '\n': + y++; + if (y >= 45) { + y--; + scroll = (scroll + 1) % 45; + XIOModule_DiscreteWrite(&gpo, 4, scroll); + clear_eol(); + } + break; + + case '\r': + x = 0; + break; + + case 0x08: + previous(); + break; + + case 0x1B: + escape = 1; + escape_parameter_1 = 0; + escape_parameter_2 = 0; + // Handle escape code + break; + + default: + write(data); + next(); + break; + } + } + XIOModule_DiscreteWrite(&gpo, 2, (y << 8) + x); + } else if (had && !io_write) { + had = 0; + XIOModule_DiscreteSet(&gpo, 3, 2); + } +} + +int main() { + XIOModule_Initialize(&gpo, XPAR_IOMODULE_0_DEVICE_ID); // Initialize the GPO module + + microblaze_register_handler(XIOModule_DeviceInterruptHandler, + XPAR_IOMODULE_0_DEVICE_ID); // register the interrupt handler + + XIOModule_Start(&gpo); // start the GPO module + + XIOModule_Connect(&gpo, XIN_IOMODULE_GPI_2_INTERRUPT_INTR, clock, + NULL); // register timerTick() as our interrupt handler + XIOModule_Enable(&gpo, XIN_IOMODULE_GPI_2_INTERRUPT_INTR); // enable the interrupt + + microblaze_enable_interrupts(); // enable global interrupts + + // Clear the screen + clear_screen(); + + XIOModule_DiscreteSet(&gpo, 3, 2); + + while (1) { + + } +}