├── overlay ├── chnlbond.v ├── diff_network.v ├── shuffle_network.v ├── DRAM16XN.v ├── hdcp_lfsr.v ├── phsaligner.v ├── hdcp_mod.v ├── hdcp_cipher.v └── delay_controller.v ├── production-images ├── README.md ├── user-35.bit ├── user-100.bit └── user-firmware.bin ├── testing-images ├── user35-pcb.bit ├── user100-pcb.bit ├── user35-cable.bit ├── user100-cable.bit ├── testing-firmware.bin └── testing-fpga.sh ├── firmware ├── bist.h ├── ci.h ├── hdmi_out0.h ├── stdio_wrap.h ├── dump.h ├── km.h ├── uptime.h ├── asm.h ├── config.h ├── stdio_wrap.c ├── config.c ├── hdmi_in1.h ├── hdmi_in0.h ├── i2c.h ├── isr.c ├── edid.h ├── pattern.h ├── linker.ld ├── processor.h ├── flags.h ├── uptime.c ├── encoder.h ├── mmcm.h ├── Makefile ├── main.c ├── bist.c ├── hdmi_out0.c ├── i2c.c ├── dump.c ├── km.c ├── pattern.c ├── encoder.c └── edid.c ├── software └── pcie │ ├── kernel │ ├── init.sh │ ├── README │ ├── flags.h │ ├── config.h │ ├── Makefile │ └── litepcie.h │ └── user │ ├── Makefile │ ├── cutils.h │ ├── litepcie_lib.h │ ├── litepcie_lib.c │ └── litepcie_util.c ├── .gitignore ├── test ├── test_identifier.py ├── xadc.py └── test_analyzer.py ├── bin ├── mkmscimg ├── litex_term ├── litex_server └── mknetv2img ├── .github └── FUNDING.yml ├── .gitmodules ├── make_images.sh ├── make_testing.sh ├── make_phases.sh ├── make_genddr.sh ├── sim ├── glbl.v ├── sim_pulsesync.py └── lxbuildenv_sim.py ├── README.adoc └── lxbuildenv.py /overlay/chnlbond.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/overlay/chnlbond.v -------------------------------------------------------------------------------- /production-images/README.md: -------------------------------------------------------------------------------- 1 | Snapshots of the latest production images as loaded in the factory. 2 | -------------------------------------------------------------------------------- /production-images/user-35.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/production-images/user-35.bit -------------------------------------------------------------------------------- /testing-images/user35-pcb.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/testing-images/user35-pcb.bit -------------------------------------------------------------------------------- /firmware/bist.h: -------------------------------------------------------------------------------- 1 | #ifndef __BIST_H 2 | #define __BIST_H 3 | 4 | void bist_test(void); 5 | 6 | #endif /* __BIST_H */ 7 | -------------------------------------------------------------------------------- /production-images/user-100.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/production-images/user-100.bit -------------------------------------------------------------------------------- /testing-images/user100-pcb.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/testing-images/user100-pcb.bit -------------------------------------------------------------------------------- /testing-images/user35-cable.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/testing-images/user35-cable.bit -------------------------------------------------------------------------------- /firmware/ci.h: -------------------------------------------------------------------------------- 1 | #ifndef __CI_H 2 | #define __CI_H 3 | 4 | void ci_prompt(void); 5 | void ci_service(void); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /testing-images/user100-cable.bit: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/testing-images/user100-cable.bit -------------------------------------------------------------------------------- /production-images/user-firmware.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/production-images/user-firmware.bin -------------------------------------------------------------------------------- /testing-images/testing-firmware.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlphamaxMedia/netv2-fpga/HEAD/testing-images/testing-firmware.bin -------------------------------------------------------------------------------- /firmware/hdmi_out0.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef CSR_HDMI_OUT0_I2C_W_ADDR 3 | 4 | void hdmi_out0_i2c_init(void); 5 | void hdmi_out0_print_edid(void); 6 | 7 | #endif -------------------------------------------------------------------------------- /firmware/stdio_wrap.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int wputs(const char *s); 4 | int wprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 5 | void wputsnonl(const char *s); 6 | -------------------------------------------------------------------------------- /software/pcie/kernel/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # TODO: use udev instead 3 | 4 | insmod litepcie.ko 5 | 6 | major=$(awk '/ litepcie$/{print $1}' /proc/devices) 7 | mknod -m 666 /dev/litepcie0 c $major 0 8 | -------------------------------------------------------------------------------- /software/pcie/kernel/README: -------------------------------------------------------------------------------- 1 | - Use 'make' to build the driver 2 | 3 | - Install the driver and create the device with : 4 | 5 | ./init.sh 6 | 7 | - Remove driver with 8 | 9 | rmmod litepcie 10 | -------------------------------------------------------------------------------- /firmware/dump.h: -------------------------------------------------------------------------------- 1 | #ifndef __DUMP_H 2 | #define __DUMP_H 3 | 4 | void mr(char *startaddr, char *len); 5 | void mw(char *addr, char *value, char *count); 6 | void mc(char *dstaddr, char *srcaddr, char *count); 7 | 8 | #endif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.d 4 | *.elf 5 | *.bin 6 | *.swp 7 | *.swo 8 | build 9 | .env 10 | .vscode 11 | test/analyzer.csv 12 | test/csr.csv 13 | .idea/ 14 | __pycache__/ 15 | pycharm.env 16 | *.upl 17 | *.vcd 18 | sim/run/ 19 | -------------------------------------------------------------------------------- /firmware/km.h: -------------------------------------------------------------------------------- 1 | void compute_keys( unsigned long Ksv_hi, unsigned long Ksv_lo, unsigned int source, unsigned long long *key ); 2 | int derive_km(void); 3 | void hdcp_isr(void); 4 | void hdcp_init(void); 5 | void init_rect(int mode, int hack); 6 | 7 | int link_redo; 8 | -------------------------------------------------------------------------------- /firmware/uptime.h: -------------------------------------------------------------------------------- 1 | #ifndef __UPTIME_H 2 | #define __UPTIME_H 3 | 4 | void uptime_service(void); 5 | int uptime(void); 6 | uint32_t uptime_ms(void); 7 | 8 | void uptime_print(void); 9 | const char* uptime_str(void); 10 | 11 | #endif /* __CONFIG_H */ 12 | -------------------------------------------------------------------------------- /software/pcie/kernel/flags.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_FLAGS_H 2 | #define __HW_FLAGS_H 3 | 4 | /* dma */ 5 | #define DMA_LOOPBACK_ENABLE 0x1 6 | 7 | #define DMA_TABLE_LOOP_INDEX 1 << 0 8 | #define DMA_TABLE_LOOP_COUNT 1 << 16 9 | 10 | #endif /* __HW_FLAGS_H */ 11 | -------------------------------------------------------------------------------- /software/pcie/kernel/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_CONFIG_H 2 | #define __HW_CONFIG_H 3 | 4 | /* pci */ 5 | #define PCI_FPGA_VENDOR_ID 0x10ee 6 | #define PCI_FPGA_DEVICE_ID 0x7022 7 | #define PCI_FPGA_BAR0_SIZE 0xa000 8 | 9 | /* dma */ 10 | #define DMA_BUFFER_COUNT 128 11 | 12 | 13 | #endif /* __HW_CONFIG_H */ 14 | -------------------------------------------------------------------------------- /firmware/asm.h: -------------------------------------------------------------------------------- 1 | #ifdef __lm32__ 2 | 3 | #define REBOOT __asm__("call r0") 4 | #define NOP __asm__("nop") 5 | 6 | #elif __or1k__ 7 | 8 | #define REBOOT __asm__("l.j 0") 9 | #define NOP __asm__("l.nop") 10 | 11 | #elif __vexriscv__ 12 | 13 | #define REBOOT __asm__("jalr x0, 0") 14 | #define NOP __asm__("addi x0, x0, 0") 15 | 16 | #else 17 | 18 | #error "Unknown ARCH." 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /software/pcie/kernel/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for kernel module 2 | 3 | KERNEL_VERSION:=$(shell uname -r) 4 | KERNEL_PATH:=/lib/modules/$(KERNEL_VERSION)/build 5 | 6 | obj-m = litepcie.o 7 | litepcie-objs = main.o 8 | 9 | all: litepcie.ko 10 | 11 | litepcie.ko: main.c 12 | make -C $(KERNEL_PATH) M=$(PWD) modules 13 | 14 | clean: 15 | make -C $(KERNEL_PATH) M=$(PWD) clean 16 | rm -f *~ 17 | -------------------------------------------------------------------------------- /software/pcie/user/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-O2 -Wall -g -I../kernel -MMD 2 | LDFLAGS=-g 3 | CC=gcc 4 | AR=ar 5 | 6 | PROGS=litepcie_util 7 | 8 | all: $(PROGS) 9 | 10 | litepcie_util: litepcie_util.o litepcie_lib.o 11 | $(CC) $(LDFLAGS) -o $@ $^ -lrt -lm 12 | 13 | clean: 14 | rm -f $(PROGS) *.o *.a *.d *~ 15 | 16 | %.o: %.c 17 | $(CC) -c $(CFLAGS) -o $@ $< 18 | 19 | -include $(wildcard *.d) 20 | -------------------------------------------------------------------------------- /test/test_identifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from litex.soc.tools.remote import RemoteClient 4 | 5 | wb = RemoteClient() 6 | wb.open() 7 | 8 | # # # 9 | 10 | # get identifier 11 | fpga_id = "" 12 | for i in range(256): 13 | c = chr(wb.read(wb.bases.identifier_mem + 4*i) & 0xff) 14 | fpga_id += c 15 | if c == "\0": 16 | break 17 | print("fpga_id: " + fpga_id) 18 | 19 | # # # 20 | 21 | wb.close() 22 | -------------------------------------------------------------------------------- /bin/mkmscimg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | 6 | # This script lives in the "bin" directory, but uses a helper script in the parent 7 | # directory. Obtain the current path so we can get the absolute parent path. 8 | script_path = os.path.dirname(os.path.realpath( 9 | __file__)) + os.path.sep + os.path.pardir + os.path.sep 10 | sys.path.insert(0, script_path) 11 | import lxbuildenv 12 | 13 | from litex.soc.tools.mkmscimg import main 14 | main() -------------------------------------------------------------------------------- /bin/litex_term: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | 6 | # This script lives in the "bin" directory, but uses a helper script in the parent 7 | # directory. Obtain the current path so we can get the absolute parent path. 8 | script_path = os.path.dirname(os.path.realpath( 9 | __file__)) + os.path.sep + os.path.pardir + os.path.sep 10 | sys.path.insert(0, script_path) 11 | import lxbuildenv 12 | 13 | from litex.soc.tools.litex_term import main 14 | main() -------------------------------------------------------------------------------- /bin/litex_server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | 6 | # This script lives in the "bin" directory, but uses a helper script in the parent 7 | # directory. Obtain the current path so we can get the absolute parent path. 8 | script_path = os.path.dirname(os.path.realpath( 9 | __file__)) + os.path.sep + os.path.pardir + os.path.sep 10 | sys.path.insert(0, script_path) 11 | import lxbuildenv 12 | 13 | from litex.soc.tools.remote.litex_server import main 14 | main() -------------------------------------------------------------------------------- /firmware/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H 2 | #define __CONFIG_H 3 | 4 | enum { 5 | CONFIG_KEY_RESOLUTION = 0, 6 | CONFIG_KEY_BLEND_USER1, 7 | CONFIG_KEY_BLEND_USER2, 8 | CONFIG_KEY_BLEND_USER3, 9 | CONFIG_KEY_BLEND_USER4, 10 | 11 | CONFIG_KEY_COUNT 12 | }; 13 | 14 | #define CONFIG_DEFAULTS { 11, 1, 2, 3, 4 } 15 | 16 | void config_init(void); 17 | void config_write_all(void); 18 | unsigned char config_get(unsigned char key); 19 | void config_set(unsigned char key, unsigned char value); 20 | 21 | #endif /* __CONFIG_H */ 22 | -------------------------------------------------------------------------------- /firmware/stdio_wrap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "stdio_wrap.h" 8 | 9 | int wputs(const char *s) 10 | { 11 | puts(s); 12 | return 0; 13 | } 14 | 15 | int wprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); 16 | int wprintf(const char *fmt, ...) 17 | { 18 | int len; 19 | va_list args; 20 | va_start(args, fmt); 21 | len = vprintf(fmt, args); 22 | va_end(args); 23 | return len; 24 | } 25 | 26 | void wputsnonl(const char *s) 27 | { 28 | putsnonl(s); 29 | } -------------------------------------------------------------------------------- /software/pcie/user/cutils.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifndef _BOOL_defined 6 | #define _BOOL_defined 7 | #undef FALSE 8 | #undef TRUE 9 | 10 | typedef int BOOL; 11 | enum { 12 | FALSE = 0, 13 | TRUE = 1, 14 | }; 15 | #endif 16 | 17 | static inline int sub_mod_int(int a, int b, int m) 18 | { 19 | a -= b; 20 | if (a < 0) 21 | a += m; 22 | return a; 23 | } 24 | 25 | static inline int add_mod_int(int a, int b, int m) 26 | { 27 | a += b; 28 | if (a >= m) 29 | a -= m; 30 | return a; 31 | } 32 | -------------------------------------------------------------------------------- /firmware/config.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "config.h" 6 | 7 | static const unsigned char config_defaults[CONFIG_KEY_COUNT] = CONFIG_DEFAULTS; 8 | static unsigned char config_values[CONFIG_KEY_COUNT]; 9 | 10 | void config_init(void) 11 | { 12 | memcpy(config_values, config_defaults, CONFIG_KEY_COUNT); 13 | } 14 | 15 | void config_write_all(void) 16 | { 17 | } 18 | 19 | unsigned char config_get(unsigned char key) 20 | { 21 | return config_values[key]; 22 | } 23 | 24 | void config_set(unsigned char key, unsigned char value) 25 | { 26 | } 27 | -------------------------------------------------------------------------------- /firmware/hdmi_in1.h: -------------------------------------------------------------------------------- 1 | #ifndef __HDMI_IN1_H 2 | #define __HDMI_IN1_H 3 | 4 | extern int hdmi_in1_debug; 5 | extern int hdmi_in1_fb_index; 6 | 7 | unsigned int hdmi_in1_framebuffer_base(char n); 8 | 9 | void hdmi_in1_isr(void); 10 | void hdmi_in1_init_video(int hres, int vres); 11 | void hdmi_in1_disable(void); 12 | void hdmi_in1_clear_framebuffers(void); 13 | void hdmi_in1_print_status(void); 14 | int hdmi_in1_calibrate_delays(int freq); 15 | int hdmi_in1_adjust_phase(void); 16 | int hdmi_in1_init_phase(void); 17 | int hdmi_in1_phase_startup(int freq); 18 | void hdmi_in1_service(int freq); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /firmware/hdmi_in0.h: -------------------------------------------------------------------------------- 1 | #ifndef __HDMI_IN0_H 2 | #define __HDMI_IN0_H 3 | 4 | extern int hdmi_in0_debug; 5 | extern int hdmi_in0_fb_index; 6 | extern int hdmi_in0_algorithm; 7 | 8 | unsigned int hdmi_in0_framebuffer_base(char n); 9 | 10 | void hdmi_in0_isr(void); 11 | void hdmi_in0_terc4_isr(void); 12 | void hdmi_in0_init_video(int hres, int vres, int freq); 13 | void hdmi_in0_disable(void); 14 | void hdmi_in0_clear_framebuffers(void); 15 | void hdmi_in0_print_status(void); 16 | int hdmi_in0_calibrate_delays(int freq); 17 | int hdmi_in0_adjust_phase(void); 18 | int hdmi_in0_init_phase(void); 19 | int hdmi_in0_phase_startup(int freq); 20 | void hdmi_in0_service(int freq); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bunnie # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /test/xadc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | 4 | from litex.soc.tools.remote import RemoteClient 5 | 6 | wb = RemoteClient() 7 | wb.open() 8 | 9 | #print("trigger_mem_full: ") 10 | #t = wb.read(0xe000b838) 11 | #print(t) 12 | 13 | print("Temperature: ") 14 | t = wb.read(0xe0005800) 15 | t <<= 8 16 | t |= wb.read(0xe0005804) 17 | print(t * 503.975 / 4096 - 273.15, "C") 18 | 19 | print("VCCint: ") 20 | t = wb.read(0xe0005808) 21 | t <<= 8 22 | t |= wb.read(0xe000580c) 23 | print(t / 0x555, "V") 24 | 25 | print("VCCaux: ") 26 | t = wb.read(0xe0005810) 27 | t <<= 8 28 | t |= wb.read(0xe0005814) 29 | print(t / 0x555, "V") 30 | 31 | print("VCCbram: ") 32 | t = wb.read(0xe0005818) 33 | t <<= 8 34 | t |= wb.read(0xe000581c) 35 | print(t / 0x555, "V") 36 | 37 | wb.close() 38 | -------------------------------------------------------------------------------- /firmware/i2c.h: -------------------------------------------------------------------------------- 1 | #ifndef __I2C_H 2 | #define __I2C_H 3 | 4 | #define I2C_SCL 0x01 5 | #define I2C_SDAOE 0x02 6 | #define I2C_SDAOUT 0x04 7 | 8 | #define I2C_SDAIN 0x01 9 | 10 | typedef unsigned char (*i2c_w_read_t)(void); 11 | typedef void (*i2c_w_write_t)(unsigned char value); 12 | typedef unsigned char (*i2c_r_read_t)(void); 13 | 14 | typedef struct { 15 | i2c_w_read_t w_read; 16 | i2c_w_write_t w_write; 17 | i2c_r_read_t r_read; 18 | int started; 19 | } I2C; 20 | 21 | int i2c_init(I2C *i2c); 22 | void i2c_delay(void); 23 | unsigned int i2c_read_bit(I2C *i2c); 24 | void i2c_write_bit(I2C *i2c, unsigned int bit); 25 | void i2c_start_cond(I2C *i2c); 26 | void i2c_stop_cond(I2C *i2c); 27 | unsigned int i2c_write(I2C *i2c, unsigned char byte); 28 | unsigned char i2c_read(I2C *i2c, int ack); 29 | 30 | #endif /* __I2C_H */ 31 | -------------------------------------------------------------------------------- /firmware/isr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hdmi_in0.h" 6 | #include "hdmi_in1.h" 7 | #include "km.h" 8 | void isr(void); 9 | void isr(void) 10 | { 11 | unsigned int irqs; 12 | 13 | hdcp_debug_write(1); 14 | irqs = irq_pending() & irq_getmask(); 15 | 16 | if(irqs & (1 << UART_INTERRUPT)) { 17 | uart_isr(); 18 | } 19 | 20 | #ifdef HDMI_IN1_INTERRUPT 21 | if(irqs & (1 << HDMI_IN1_INTERRUPT)) { 22 | hdmi_in1_isr(); 23 | } 24 | #endif 25 | #ifdef HDCP_INTERRUPT 26 | if(irqs & (1 << HDCP_INTERRUPT)) { 27 | hdcp_isr(); 28 | } 29 | #endif 30 | #ifdef CSR_HDMI_IN0_DECODE_TERC4_EV_ENABLE_ADDR 31 | if(irqs & (1 << HDMI_IN0_INTERRUPT)) { 32 | hdmi_in0_terc4_isr(); // actually handling a terc4 decode, not a DMA ISR. Awful API, I know. 33 | } 34 | #endif 35 | 36 | hdcp_debug_write(0); 37 | } 38 | -------------------------------------------------------------------------------- /firmware/edid.h: -------------------------------------------------------------------------------- 1 | #ifndef __EDID_H 2 | #define __EDID_H 3 | 4 | #define MAX_MONITOR_NAME_LEN 13 5 | 6 | struct video_timing { 7 | unsigned int pixel_clock; /* in tens of kHz */ 8 | 9 | unsigned int h_active; 10 | unsigned int h_blanking; 11 | unsigned int h_sync_offset; 12 | unsigned int h_sync_width; 13 | 14 | unsigned int v_active; 15 | unsigned int v_blanking; 16 | unsigned int v_sync_offset; 17 | unsigned int v_sync_width; 18 | 19 | unsigned int established_timing; 20 | const char* comment; 21 | }; 22 | 23 | int validate_edid(const void *buf); 24 | void get_monitor_name(const void *buf, char *name); 25 | void generate_edid(void *out, 26 | const char mfg_name[3], const char product_code[2], int year, 27 | const char *name, 28 | const struct video_timing *timing); 29 | 30 | unsigned calculate_refresh_rate(const struct video_timing* video_mode); 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/litex"] 2 | path = deps/litex 3 | url = https://github.com/alphamaxmedia/litex.git 4 | branch = master 5 | [submodule "deps/migen"] 6 | path = deps/migen 7 | url = https://github.com/alphamaxmedia/migen.git 8 | branch = improve-gearbox-timing 9 | [submodule "deps/litedram"] 10 | path = deps/litedram 11 | url = https://github.com/alphamaxmedia/litedram.git 12 | [submodule "deps/litevideo"] 13 | path = deps/litevideo 14 | url = https://github.com/alphamaxmedia/litevideo.git 15 | branch = terc4-data 16 | [submodule "deps/liteeth"] 17 | path = deps/liteeth 18 | url = https://github.com/enjoy-digital/liteeth.git 19 | [submodule "deps/litescope"] 20 | path = deps/litescope 21 | url = https://github.com/alphamaxmedia/litescope.git 22 | branch = add-trigger-depth 23 | [submodule "deps/pyserial"] 24 | path = deps/pyserial 25 | url = https://github.com/pyserial/pyserial.git 26 | -------------------------------------------------------------------------------- /firmware/pattern.h: -------------------------------------------------------------------------------- 1 | #ifndef __PATTERN_H 2 | #define __PATTERN_H 3 | 4 | /* Colors in YCBCR422 format (see pattern.py) */ 5 | #define YCBCR422_WHITE 0x80ff80ff 6 | #define YCBCR422_YELLOW 0x00e194e1 7 | #define YCBCR422_CYAN 0xabb200b2 8 | #define YCBCR422_GREEN 0x2b951595 9 | #define YCBCR422_PURPLE 0xd469e969 10 | #define YCBCR422_RED 0x544cff4c 11 | #define YCBCR422_BLUE 0xff1d6f1d 12 | #define YCBCR422_BLACK 0x80108010 13 | 14 | /* Colors in RGB format */ 15 | #define RGB_WHITE 0x00ffffff 16 | #define RGB_YELLOW 0x0000ffff 17 | #define RGB_CYAN 0x00ffff00 18 | #define RGB_GREEN 0x0000ff00 19 | #define RGB_PURPLE 0x00ff00ff 20 | #define RGB_RED 0x000000ff 21 | #define RGB_BLUE 0x00ff0000 22 | #define RGB_BLACK 0x00000000 23 | 24 | unsigned int pattern_framebuffer_base(void); 25 | 26 | int pattern; 27 | 28 | #define COLOR_BAR_PATTERN 0 29 | #define BLACK_WHITE_BAR_PATTERN 1 30 | 31 | void pattern_fill_framebuffer(int h_active, int m_active); 32 | void pattern_service(void); 33 | 34 | #endif /* __PATTERN_H */ 35 | -------------------------------------------------------------------------------- /test/test_analyzer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from litex.soc.tools.remote import RemoteClient 4 | 5 | from litescope import LiteScopeAnalyzerDriver 6 | 7 | wb = RemoteClient() 8 | wb.open() 9 | 10 | # # # 11 | 12 | analyzer = LiteScopeAnalyzerDriver(wb.regs, "analyzer", debug=True) 13 | analyzer.configure_subsampler(1) 14 | analyzer.configure_group(0) 15 | 16 | #analyzer.add_trigger(cond={"soc_videooverlaysoc_videooverlaysoc_videooverlaysoc_interrupt": 0x8}) 17 | #analyzer.add_rising_edge_trigger("soc_videooverlaysoc_videooverlaysoc_videooverlaysoc_interrupt") 18 | #analyzer.add_falling_edge_trigger("soc_videooverlaysoc_hdmi_in1_frame_de") 19 | analyzer.add_falling_edge_trigger("soc_videooverlaysoc_hdmi_in1_frame_de") 20 | #analyzer.add_trigger(cond={"soc_videooverlaysoc_hdcp_ctl_code" : 9}) 21 | #analyzer.add_trigger(cond={"soc_videooverlaysoc_analyzer_fsm2_state" : 0}) 22 | 23 | analyzer.run(offset=32, length=64) 24 | analyzer.wait_done() 25 | analyzer.upload() 26 | analyzer.save("dump.vcd") 27 | 28 | # # # 29 | 30 | wb.close() 31 | -------------------------------------------------------------------------------- /firmware/linker.ld: -------------------------------------------------------------------------------- 1 | INCLUDE generated/output_format.ld 2 | ENTRY(_start) 3 | 4 | __DYNAMIC = 0; 5 | 6 | INCLUDE generated/regions.ld 7 | 8 | SECTIONS 9 | { 10 | .text : 11 | { 12 | _ftext = .; 13 | *(.text .stub .text.* .gnu.linkonce.t.*) 14 | _etext = .; 15 | } > main_ram 16 | 17 | .rodata : 18 | { 19 | . = ALIGN(4); 20 | _frodata = .; 21 | *(.rodata .rodata.* .gnu.linkonce.r.*) 22 | *(.rodata1) 23 | _erodata = .; 24 | } > main_ram 25 | 26 | .data : 27 | { 28 | . = ALIGN(4); 29 | _fdata = .; 30 | *(.data .data.* .gnu.linkonce.d.*) 31 | *(.data1) 32 | _gp = ALIGN(16); 33 | *(.sdata .sdata.* .gnu.linkonce.s.*) 34 | _edata = .; 35 | } > main_ram 36 | 37 | .bss : 38 | { 39 | . = ALIGN(4); 40 | _fbss = .; 41 | *(.dynsbss) 42 | *(.sbss .sbss.* .gnu.linkonce.sb.*) 43 | *(.scommon) 44 | *(.dynbss) 45 | *(.bss .bss.* .gnu.linkonce.b.*) 46 | *(COMMON) 47 | . = ALIGN(4); 48 | _ebss = .; 49 | _end = .; 50 | } > sram 51 | } 52 | 53 | PROVIDE(_fstack = ORIGIN(main_ram) + LENGTH(main_ram) - 4); 54 | -------------------------------------------------------------------------------- /make_images.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PYTHONHASHSEED=1 4 | 5 | if [ -z "$1" ] 6 | then 7 | RUN="all" 8 | echo "None of 100T or 35T specified, so building all variants." 9 | else 10 | if [ "$1" = "help" ] 11 | then 12 | echo "Valid build options: 100T or 35T. Blank is the same as all." 13 | exit 0 14 | fi 15 | RUN="$1" 16 | echo "Run set to $1." 17 | fi 18 | 19 | if [ "$RUN" = "all" ] || [ "$RUN" = "35T" ] 20 | then 21 | echo "Running user 35T FPGA build..." 22 | ./netv2mvp.py -p 35 -t video_overlay 23 | cp ./build/gateware/top.bit ./production-images/user-35.bit 24 | fi 25 | 26 | if [ "$RUN" = "all" ] || [ "$RUN" = "100T" ] 27 | then 28 | echo "Running user 100T FPGA build..." 29 | ./netv2mvp.py -p 100 -t video_overlay 30 | cp ./build/gateware/top.bit ./production-images/user-100.bit 31 | fi 32 | 33 | echo "Running user firmware build..." 34 | cd ./firmware && make clean && make && cd .. 35 | cp ./firmware/firmware.bin ./production-images/user-firmware.bin 36 | 37 | echo "All requested builds done." 38 | 39 | exit 0 40 | -------------------------------------------------------------------------------- /firmware/processor.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROCESSOR_H 2 | #define __PROCESSOR_H 3 | 4 | #define PROCESSOR_MODE_COUNT 16 5 | #define PROCESSOR_MODE_DESCLEN 64 6 | 7 | enum { 8 | VIDEO_IN_HDMI_IN0=0, 9 | VIDEO_IN_HDMI_IN1, 10 | VIDEO_IN_PATTERN 11 | }; 12 | 13 | enum { 14 | VIDEO_OUT_HDMI_OUT0=0, 15 | VIDEO_OUT_HDMI_OUT1, 16 | VIDEO_OUT_ENCODER 17 | }; 18 | 19 | extern int processor_mode; 20 | int processor_h_active; 21 | int processor_v_active; 22 | int processor_refresh; 23 | int processor_hdmi_out0_source; 24 | int processor_hdmi_out1_source; 25 | int processor_encoder_source; 26 | char processor_buffer[16]; 27 | 28 | void processor_list_modes(char *mode_descriptors); 29 | void processor_init(void); 30 | void processor_start(int mode); 31 | void processor_set_hdmi_out0_source(int source); 32 | void processor_set_hdmi_out1_source(int source); 33 | void processor_set_encoder_source(int source); 34 | char * processor_get_source_name(int source); 35 | void processor_update(void); 36 | void processor_service(void); 37 | 38 | void processor_set_hdmi_in0_pixclk(int freq); 39 | #endif /* __PROCESSOR_H */ 40 | -------------------------------------------------------------------------------- /firmware/flags.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_FLAGS_H 2 | #define __HW_FLAGS_H 3 | 4 | #define UART_EV_TX 0x1 5 | #define UART_EV_RX 0x2 6 | 7 | #define DFII_CONTROL_SEL 0x01 8 | #define DFII_CONTROL_CKE 0x02 9 | #define DFII_CONTROL_ODT 0x04 10 | #define DFII_CONTROL_RESET_N 0x08 11 | 12 | #define DFII_COMMAND_CS 0x01 13 | #define DFII_COMMAND_WE 0x02 14 | #define DFII_COMMAND_CAS 0x04 15 | #define DFII_COMMAND_RAS 0x08 16 | #define DFII_COMMAND_WRDATA 0x10 17 | #define DFII_COMMAND_RDDATA 0x20 18 | 19 | #define ETHMAC_EV_SRAM_WRITER 0x1 20 | #define ETHMAC_EV_SRAM_READER 0x1 21 | 22 | #define CLKGEN_STATUS_BUSY 0x1 23 | #define CLKGEN_STATUS_PROGDONE 0x2 24 | #define CLKGEN_STATUS_LOCKED 0x4 25 | 26 | #define DVISAMPLER_TOO_LATE 0x1 27 | #define DVISAMPLER_TOO_EARLY 0x2 28 | 29 | #define DVISAMPLER_DELAY_RST 0x01 30 | #define DVISAMPLER_DELAY_MASTER_INC 0x02 31 | #define DVISAMPLER_DELAY_MASTER_DEC 0x04 32 | #define DVISAMPLER_DELAY_SLAVE_INC 0x08 33 | #define DVISAMPLER_DELAY_SLAVE_DEC 0x10 34 | 35 | #define DVISAMPLER_SLOT_EMPTY 0 36 | #define DVISAMPLER_SLOT_LOADED 1 37 | #define DVISAMPLER_SLOT_PENDING 2 38 | 39 | #endif /* __HW_FLAGS_H */ 40 | -------------------------------------------------------------------------------- /firmware/uptime.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "uptime.h" 8 | 9 | #include "stdio_wrap.h" 10 | 11 | static int uptime_seconds = 0; 12 | static int last_event; 13 | 14 | void uptime_service(void) 15 | { 16 | if(elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY)) { 17 | uptime_seconds++; 18 | } 19 | } 20 | 21 | // return ms since boot 22 | // doesn't gracefully handle overflow, so perhaps once in 49 days the system will glitch 23 | uint32_t uptime_ms(void) { 24 | int timer_cur; 25 | int delta; 26 | 27 | elapsed(&timer_cur, -1); 28 | delta = timer_cur - last_event; 29 | if( delta < 0 ) 30 | delta += timer0_reload_read(); 31 | 32 | return uptime_seconds * 1000 + (delta / (SYSTEM_CLOCK_FREQUENCY / 1000)); 33 | } 34 | 35 | int uptime(void) 36 | { 37 | return uptime_seconds; 38 | } 39 | 40 | void uptime_print(void) 41 | { 42 | wprintf("uptime: %s\n", uptime_str()); 43 | } 44 | 45 | const char* uptime_str(void) 46 | { 47 | static char buffer[9]; 48 | sprintf(buffer, "%02d:%02d:%02d", 49 | (uptime_seconds/3600)%24, 50 | (uptime_seconds/60)%60, 51 | uptime_seconds%60); 52 | return buffer; 53 | } 54 | -------------------------------------------------------------------------------- /firmware/encoder.h: -------------------------------------------------------------------------------- 1 | #ifndef __ENCODER_H 2 | #define __ENCODER_H 3 | 4 | #define ENCODER_START_REG 0x0 5 | #define ENCODER_IMAGE_SIZE_REG 0x4 6 | #define ENCODER_RAM_ACCESS_REG 0x8 7 | #define ENCODER_STS_REG 0xC 8 | #define ENCODER_COD_DATA_ADDR_REG 0x10 9 | #define ENCODER_LENGTH_REG 0x14 10 | 11 | #define ENCODER_QUANTIZER_RAM_LUMA_BASE 0x100 12 | 13 | const char luma_rom_100[64]; 14 | const char luma_rom_85[64]; 15 | const char luma_rom_75[64]; 16 | const char luma_rom_50[64]; 17 | 18 | #define ENCODER_QUANTIZER_RAM_CHROMA_BASE 0x200 19 | 20 | const char chroma_rom_100[64]; 21 | const char chroma_rom_85[64]; 22 | const char chroma_rom_75[64]; 23 | const char chroma_rom_50[64]; 24 | 25 | char encoder_enabled; 26 | int encoder_target_fps; 27 | int encoder_fps; 28 | int encoder_quality; 29 | 30 | void encoder_write_reg(unsigned int adr, unsigned int value); 31 | unsigned int encoder_read_reg(unsigned int adr); 32 | void encoder_init(int encoder_quality); 33 | void encoder_start(short resx, short resy); 34 | int encoder_done(void); 35 | void encoder_enable(char enable); 36 | int encoder_set_quality(int quality); 37 | int encoder_set_fps(int fps); 38 | void encoder_service(void); 39 | 40 | #endif -------------------------------------------------------------------------------- /make_testing.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PYTHONHASHSEED=1 4 | 5 | if [ -z "$1" ] 6 | then 7 | RUN="all" 8 | echo "None of 100T or 35T specified, so building all variants." 9 | else 10 | if [ "$1" = "help" ] 11 | then 12 | echo "Valid build options: 100T or 35T. Blank is the same as all." 13 | exit 0 14 | fi 15 | RUN="$1" 16 | echo "Run set to $1." 17 | fi 18 | 19 | declare -a cables=("pcb" "cable") 20 | 21 | if [ "$RUN" = "all" ] || [ "$RUN" = "35T" ] 22 | then 23 | echo "Running user 35T FPGA build..." 24 | for cable in "${cables[@]}" 25 | do 26 | ./netv2mvp.py -p 35 -t video_overlay -d 112.5 -c $cable 27 | cp ./build/gateware/top.bit ./testing-images/user35-$cable.bit 28 | done 29 | fi 30 | 31 | if [ "$RUN" = "all" ] || [ "$RUN" = "100T" ] 32 | then 33 | echo "Running user 100T FPGA build..." 34 | for cable in "${cables[@]}" 35 | do 36 | ./netv2mvp.py -p 100 -t video_overlay -d 112.5 -c $cable 37 | cp ./build/gateware/top.bit ./testing-images/user100-$cable.bit 38 | done 39 | fi 40 | 41 | echo "Running user firmware build..." 42 | cd ./firmware && make clean && make && cd .. 43 | cp ./firmware/firmware.bin ./testing-images/testing-firmware.bin 44 | 45 | echo "All requested builds done." 46 | 47 | exit 0 48 | -------------------------------------------------------------------------------- /make_phases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PYTHONHASHSEED=1 4 | 5 | if [ -z "$1" ] 6 | then 7 | RUN="all" 8 | echo "None of 100T or 35T specified, so building all variants." 9 | else 10 | if [ "$1" = "help" ] 11 | then 12 | echo "Valid build options: 100T or 35T. Blank is the same as all." 13 | exit 0 14 | fi 15 | RUN="$1" 16 | echo "Run set to $1." 17 | fi 18 | 19 | declare -a phases=("45.0" "67.5" "90.0" "112.5" "135.0" "157.5" "180.0") 20 | 21 | if [ "$RUN" = "all" ] || [ "$RUN" = "100T" ] 22 | then 23 | echo "Running user 100T FPGA build..." 24 | for phase in "${phases[@]}" 25 | do 26 | ./netv2mvp.py -p 100 -t video_overlay -d $phase 27 | cp ./build/gateware/top.bit ./phase-images/user100-$phase.bit 28 | done 29 | fi 30 | 31 | if [ "$RUN" = "all" ] || [ "$RUN" = "35T" ] 32 | then 33 | echo "Running user 35T FPGA build..." 34 | for phase in "${phases[@]}" 35 | do 36 | ./netv2mvp.py -p 35 -t video_overlay -d $phase 37 | cp ./build/gateware/top.bit ./phase-images/user35-$phase.bit 38 | done 39 | fi 40 | 41 | echo "Running user firmware build..." 42 | cd ./firmware && make clean && make && cd .. 43 | cp ./firmware/firmware.bin ./phase-images/phase-firmware.bin 44 | 45 | echo "All requested builds done." 46 | 47 | exit 0 48 | -------------------------------------------------------------------------------- /firmware/mmcm.h: -------------------------------------------------------------------------------- 1 | #ifndef __MMCM_H 2 | #define __MMCM_H 3 | 4 | void mmcm_decode_clkreg1(unsigned int data); 5 | void mmcm_decode_clkreg2(unsigned int data); 6 | void mmcm_decode_divreg(unsigned int data); 7 | void mmcm_decode_lockreg1(unsigned int data); 8 | void mmcm_decode_lockreg2(unsigned int data); 9 | void mmcm_decode_lockreg3(unsigned int data); 10 | void mmcm_decode_filtreg1(unsigned int data); 11 | void mmcm_decode_filtreg2(unsigned int data); 12 | void mmcm_decode_power7(unsigned int data); 13 | void mmcm_decode_reg(unsigned int adr, unsigned int data); 14 | 15 | void hdmi_out0_mmcm_write(int adr, int data); 16 | int hdmi_out0_mmcm_read(int adr); 17 | 18 | #ifdef CSR_HDMI_IN0_CLOCKING_MMCM_DRDY_O_ADDR 19 | void hdmi_in0_clocking_mmcm_write_o(int adr, int data); 20 | int hdmi_in0_clocking_mmcm_read_o(int adr); 21 | void hdmi_in0_clocking_mmcm_write(int adr, int data); 22 | int hdmi_in0_clocking_mmcm_read(int adr); 23 | #endif 24 | 25 | #ifdef CSR_HDMI_IN1_CLOCKING_MMCM_DRDY_O_ADDR 26 | void hdmi_in1_clocking_mmcm_write_o(int adr, int data); 27 | int hdmi_in1_clocking_mmcm_read_o(int adr); 28 | void hdmi_in1_clocking_mmcm_write(int adr, int data); 29 | int hdmi_in1_clocking_mmcm_read(int adr); 30 | #endif 31 | 32 | void mmcm_config_for_clock(int freq); 33 | void mmcm_dump(void); 34 | void mmcm_dump_code(void); 35 | 36 | void hdmi_in_0_config_60_120mhz_table(); 37 | void hdmi_in_0_config_120_240mhz_table(); 38 | #endif 39 | -------------------------------------------------------------------------------- /firmware/Makefile: -------------------------------------------------------------------------------- 1 | NETV2_DIR=../build 2 | 3 | include $(NETV2_DIR)/software/include/generated/variables.mak 4 | include $(SOC_DIRECTORY)/software/common.mak 5 | 6 | ifeq ($(OS),Windows_NT) 7 | COPY := cmd /c copy 8 | else 9 | COPY := cp 10 | endif 11 | 12 | OBJECTS=isr.o \ 13 | config.o \ 14 | processor.o \ 15 | hdmi_in0.o \ 16 | hdmi_in1.o \ 17 | hdmi_out0.o \ 18 | pattern.o \ 19 | edid.o \ 20 | mmcm.o \ 21 | ci.o \ 22 | encoder.o \ 23 | i2c.o \ 24 | main.o \ 25 | bist.o \ 26 | dump.o \ 27 | km.o \ 28 | compute_ksv.o \ 29 | stdio_wrap.o \ 30 | uptime.o 31 | 32 | CFLAGS += -I. 33 | 34 | all: firmware.bin 35 | 36 | # pull in dependency info for *existing* .o files 37 | -include $(OBJECTS:.o=.d) 38 | 39 | %.bin: %.elf 40 | $(OBJCOPY) -O binary $< $@ 41 | ifneq ($(OS),Windows_NT) 42 | chmod -x $@ 43 | endif 44 | $(COPY) $@ boot.bin 45 | 46 | firmware.elf: $(OBJECTS) 47 | $(LD) $(LDFLAGS) \ 48 | -T linker.ld \ 49 | -N -o $@ \ 50 | $(NETV2_DIR)/software/libbase/crt0-$(CPU)-ctr.o \ 51 | $(OBJECTS) \ 52 | -L$(NETV2_DIR)/software/libbase \ 53 | -L$(NETV2_DIR)/software/libcompiler_rt \ 54 | -lbase-nofloat -lcompiler_rt 55 | ifneq ($(OS),Windows_NT) 56 | chmod -x $@ 57 | endif 58 | 59 | main.o: main.c 60 | $(compile) 61 | 62 | %.o: %.c 63 | $(compile) 64 | 65 | %.o: %.S 66 | $(assemble) 67 | 68 | load: firmware.bin 69 | litex_term --kernel firmware.bin COM8 70 | 71 | clean: 72 | $(RM) $(OBJECTS) $(OBJECTS:.o=.d) firmware.elf firmware.bin .*~ *~ 73 | 74 | .PHONY: all main.o clean load 75 | -------------------------------------------------------------------------------- /make_genddr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PYTHONHASHSEED=1 4 | 5 | if [ -z "$1" ] 6 | then 7 | RUN="all" 8 | echo "None of 100T or 35T specified, so building all variants." 9 | else 10 | if [ "$1" = "help" ] 11 | then 12 | echo "Valid build options: 100T or 35T. Blank is the same as all." 13 | exit 0 14 | fi 15 | RUN="$1" 16 | echo "Run set to $1." 17 | fi 18 | 19 | declare -a interm=("NONE" "40" "50" "60") 20 | declare -a strengths=("34ohm" "40ohm") 21 | 22 | if [ "$RUN" = "all" ] || [ "$RUN" = "100T" ] 23 | then 24 | echo "Running user 100T FPGA build..." 25 | for term in "${interm[@]}" 26 | do 27 | for strength in "${strengths[@]}" 28 | do 29 | ./netv2mvp_genddr.py -p 100 -t video_overlay -d 112.5 -i $term -s $strength 30 | cp ./build/gateware/top.bit ./genddr-images/user100-f$term-d$strength.bit 31 | done 32 | done 33 | fi 34 | 35 | if [ "$RUN" = "all" ] || [ "$RUN" = "35T" ] 36 | then 37 | echo "Running user 35T FPGA build..." 38 | for term in "${interm[@]}" 39 | do 40 | for strength in "${strengths[@]}" 41 | do 42 | # echo user35-f$term-d$strength.bit 43 | ./netv2mvp_genddr.py -p 35 -t video_overlay -d 112.5 -i $term -s $strength 44 | cp ./build/gateware/top.bit ./genddr-images/user35-f$term-d$strength.bit 45 | done 46 | done 47 | fi 48 | 49 | echo "Running user firmware build..." 50 | cd ./firmware && make clean && make && cd .. 51 | cp ./firmware/firmware.bin ./genddr-images/genddr-firmware.bin 52 | 53 | echo "All requested builds done." 54 | 55 | exit 0 56 | -------------------------------------------------------------------------------- /sim/glbl.v: -------------------------------------------------------------------------------- 1 | // $Header: /devl/xcs/repo/env/Databases/CAEInterfaces/verunilibs/data/glbl.v,v 1.14 2010/10/28 20:44:00 fphillip Exp $ 2 | `ifndef GLBL 3 | `define GLBL 4 | `timescale 1 ps / 1 ps 5 | 6 | module glbl (); 7 | 8 | parameter ROC_WIDTH = 100000; 9 | parameter TOC_WIDTH = 0; 10 | 11 | //-------- STARTUP Globals -------------- 12 | wire GSR; 13 | wire GTS; 14 | wire GWE; 15 | wire PRLD; 16 | tri1 p_up_tmp; 17 | tri (weak1, strong0) PLL_LOCKG = p_up_tmp; 18 | 19 | wire PROGB_GLBL; 20 | wire CCLKO_GLBL; 21 | wire FCSBO_GLBL; 22 | wire [3:0] DO_GLBL; 23 | wire [3:0] DI_GLBL; 24 | 25 | reg GSR_int; 26 | reg GTS_int; 27 | reg PRLD_int; 28 | 29 | //-------- JTAG Globals -------------- 30 | wire JTAG_TDO_GLBL; 31 | wire JTAG_TCK_GLBL; 32 | wire JTAG_TDI_GLBL; 33 | wire JTAG_TMS_GLBL; 34 | wire JTAG_TRST_GLBL; 35 | 36 | reg JTAG_CAPTURE_GLBL; 37 | reg JTAG_RESET_GLBL; 38 | reg JTAG_SHIFT_GLBL; 39 | reg JTAG_UPDATE_GLBL; 40 | reg JTAG_RUNTEST_GLBL; 41 | 42 | reg JTAG_SEL1_GLBL = 0; 43 | reg JTAG_SEL2_GLBL = 0 ; 44 | reg JTAG_SEL3_GLBL = 0; 45 | reg JTAG_SEL4_GLBL = 0; 46 | 47 | reg JTAG_USER_TDO1_GLBL = 1'bz; 48 | reg JTAG_USER_TDO2_GLBL = 1'bz; 49 | reg JTAG_USER_TDO3_GLBL = 1'bz; 50 | reg JTAG_USER_TDO4_GLBL = 1'bz; 51 | 52 | assign (weak1, weak0) GSR = GSR_int; 53 | assign (weak1, weak0) GTS = GTS_int; 54 | assign (weak1, weak0) PRLD = PRLD_int; 55 | 56 | initial begin 57 | GSR_int = 1'b1; 58 | PRLD_int = 1'b1; 59 | #(ROC_WIDTH) 60 | GSR_int = 1'b0; 61 | PRLD_int = 1'b0; 62 | end 63 | 64 | initial begin 65 | GTS_int = 1'b1; 66 | #(TOC_WIDTH) 67 | GTS_int = 1'b0; 68 | end 69 | 70 | endmodule 71 | `endif 72 | -------------------------------------------------------------------------------- /software/pcie/kernel/litepcie.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LitePCIe driver 3 | * 4 | */ 5 | #ifndef _LINUX_LITEPCIE_H 6 | #define _LINUX_LITEPCIE_H 7 | 8 | #include 9 | 10 | struct litepcie_ioctl_mmap_info { 11 | unsigned long reg_offset; 12 | unsigned long reg_size; 13 | 14 | unsigned long dma_tx_buf_offset; 15 | unsigned long dma_tx_buf_size; 16 | unsigned long dma_tx_buf_count; 17 | 18 | unsigned long dma_rx_buf_offset; 19 | unsigned long dma_rx_buf_size; 20 | unsigned long dma_rx_buf_count; 21 | }; 22 | 23 | struct litepcie_ioctl_dma_start { 24 | __u32 dma_flags; /* see LITEPCIE_DMA_FLAGS_x */ 25 | __u32 tx_buf_size; /* in bytes, must be < dma_buf_pitch. 0 means no TX */ 26 | __u32 tx_buf_count; 27 | __u32 rx_buf_size; /* in bytes, must be < dma_buf_pitch. 0 means no RX */ 28 | __u32 rx_buf_count; 29 | }; 30 | 31 | /* if tx_wait is true, wait until the current TX bufffer is 32 | different from tx_buf_num. If tx_wait is false, wait until the 33 | current RX buffer is different from rx_buf_num. Return the last 34 | TX buffer in tx_buf_num and the last RX buffer in 35 | rx_buf_num. */ 36 | struct litepcie_ioctl_dma_wait { 37 | __s32 timeout; /* in ms. Return -EAGAIN if timeout occured without event */ 38 | __u32 tx_wait; 39 | __u32 tx_buf_num; /* read/write */ 40 | __u32 rx_buf_num; /* read/write */ 41 | }; 42 | 43 | #define LITEPCIE_IOCTL 'S' 44 | 45 | #define LITEPCIE_IOCTL_GET_MMAP_INFO _IOR(LITEPCIE_IOCTL, 0, struct litepcie_ioctl_mmap_info) 46 | #define LITEPCIE_IOCTL_DMA_START _IOW(LITEPCIE_IOCTL, 1, struct litepcie_ioctl_dma_start) 47 | #define LITEPCIE_IOCTL_DMA_STOP _IO(LITEPCIE_IOCTL, 2) 48 | #define LITEPCIE_IOCTL_DMA_WAIT _IOWR(LITEPCIE_IOCTL, 3, struct litepcie_ioctl_dma_wait) 49 | 50 | #endif /* _LINUX_LITEPCIE_H */ 51 | -------------------------------------------------------------------------------- /bin/mknetv2img: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import binascii 5 | 6 | 7 | def insert_crc(i_filename, fbi_mode=False, o_filename=None, little_endian=False): 8 | endian = "little" if little_endian else "big" 9 | 10 | if o_filename is None: 11 | o_filename = i_filename 12 | 13 | with open(i_filename, "rb") as f: 14 | fdata = f.read() 15 | fcrc = binascii.crc32(fdata).to_bytes(4, byteorder=endian) 16 | flength = len(fdata).to_bytes(4, byteorder=endian) 17 | 18 | i = 0 19 | temp = bytearray(4) 20 | o_array = bytearray() 21 | for x in fdata: 22 | temp[i] = x 23 | i = i + 1 24 | if i == 4: 25 | o_array.append(temp[3]) 26 | o_array.append(temp[2]) 27 | o_array.append(temp[1]) 28 | o_array.append(temp[0]) 29 | i = 0 30 | 31 | 32 | with open(o_filename, "wb") as f: 33 | if fbi_mode: 34 | f.write(flength) 35 | f.write(fcrc) 36 | f.write(o_array) 37 | else: 38 | f.write(fdata) 39 | f.write(fcrc) 40 | 41 | 42 | def main(): 43 | parser = argparse.ArgumentParser(description="CRC32 computation tool and MiSoC image file writer.") 44 | parser.add_argument("input", help="input file") 45 | parser.add_argument("-o", "--output", default=None, help="output file (if not specified, use input file)") 46 | parser.add_argument("-f", "--fbi", default=False, action="store_true", help="build flash boot image (FBI) file") 47 | parser.add_argument("-l", "--little", default=False, action="store_true", help="Use little endian to write the CRC32") 48 | args = parser.parse_args() 49 | insert_crc(args.input, args.fbi, args.output, args.little) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /firmware/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "flags.h" 11 | #include 12 | #include 13 | 14 | #include "config.h" 15 | #include "ci.h" 16 | #include "processor.h" 17 | #include "pattern.h" 18 | 19 | #include "mmcm.h" 20 | #include "km.h" 21 | 22 | void * __stack_chk_guard = (void *) (0xDEADBEEF); 23 | void __stack_chk_fail(void) { 24 | printf( "stack fail\n" ); 25 | } 26 | 27 | int main(void) 28 | { 29 | hdcp_hpd_ena_write(1); // de-assert hot plug detect while booting 30 | irq_setmask(0); 31 | irq_setie(1); 32 | uart_init(); 33 | 34 | // hdmi_out0_i2c_init(); 35 | 36 | puts("\nNeTV2 software built "__DATE__" "__TIME__); 37 | 38 | // mmcm_dump_code(); // call this to dump the PLL config out based on vivado compiled constants 39 | 40 | config_init(); 41 | time_init(); 42 | 43 | processor_init(); 44 | processor_update(); 45 | processor_start(config_get(CONFIG_KEY_RESOLUTION)); 46 | 47 | ci_prompt(); 48 | 49 | int wait_event; 50 | int hpd_wait = 0; 51 | int waiting = 1; 52 | elapsed(&wait_event, SYSTEM_CLOCK_FREQUENCY); 53 | 54 | hdcp_hpd_ena_write(0); // release hot plug detect once we're into the main loop 55 | 56 | while(1) { 57 | processor_service(); 58 | ci_service(); 59 | uptime_service(); 60 | 61 | // put a delay of a few seconds before releasing HPD 62 | if( waiting ) { 63 | if( elapsed( &wait_event, SYSTEM_CLOCK_FREQUENCY ) ) { 64 | hpd_wait++; 65 | } 66 | if( hpd_wait > 1 ) { 67 | hdcp_hpd_ena_write(0); // release hot plug detect once we're into the main loop 68 | waiting = 0; 69 | } 70 | } 71 | 72 | if( link_redo ) { 73 | waiting = 1; 74 | hpd_wait = 0; 75 | link_redo = 0; 76 | hdcp_hpd_ena_write(1); 77 | } 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /software/pcie/user/litepcie_lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LitePCIe library 3 | * 4 | */ 5 | #ifndef LITEPCIE_LIB_H 6 | #define LITEPCIE_LIB_H 7 | 8 | #include 9 | #include 10 | 11 | #define LITEPCIE_FILENAME "/dev/litepcie0" 12 | 13 | typedef struct { 14 | int litepcie_fd; 15 | struct litepcie_ioctl_mmap_info mmap_info; 16 | uint8_t *dma_tx_buf; 17 | int dma_tx_buf_size; 18 | uint8_t *dma_rx_buf; 19 | int dma_rx_buf_size; 20 | uint8_t *reg_buf; 21 | 22 | unsigned int tx_buf_size; /* in bytes */ 23 | unsigned int tx_buf_count; /* number of buffers */ 24 | unsigned int rx_buf_size; /* in bytes */ 25 | unsigned int rx_buf_count; /* number of buffers */ 26 | 27 | unsigned int tx_buf_len; /* in samples */ 28 | unsigned int rx_buf_len; /* in samples */ 29 | 30 | pthread_mutex_t fifo_mutex; 31 | int64_t rx_timestamp; /* timestamp (in samples) of the current RX buffer */ 32 | unsigned int rx_buf_index; /* index of the current RX buffer */ 33 | unsigned int rx_buf_next; /* index of the next buffer after the 34 | last received buffer */ 35 | BOOL has_rx_timestamp; /* true if received at least one buffer */ 36 | 37 | int64_t tx_underflow_count; /* TX too late */ 38 | int64_t rx_overflow_count; /* RX too late */ 39 | } LitePCIeState; 40 | 41 | void *litepcie_malloc(int size); 42 | void *litepcie_mallocz(int size); 43 | void litepcie_free(void *ptr); 44 | void __attribute__((format(printf, 2, 3))) litepcie_log(LitePCIeState *s, const char *fmt, ...); 45 | int64_t litepcie_get_time_ms(void); 46 | LitePCIeState *litepcie_open(const char *device_name); 47 | void litepcie_close(LitePCIeState *s); 48 | void litepcie_dma_start(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback); 49 | void litepcie_dma_stop(LitePCIeState *s); 50 | void litepcie_writel(LitePCIeState *s, uint32_t addr, uint32_t val); 51 | uint32_t litepcie_readl(LitePCIeState *s, uint32_t addr); 52 | 53 | #endif /* LITEPCIE_LIB_H */ 54 | -------------------------------------------------------------------------------- /overlay/diff_network.v: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2011, Andrew "bunnie" Huang 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, 9 | // this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | // POSSIBILITY OF SUCH DAMAGE. 24 | // 25 | ////////////////////////////////////////////////////////////////////////////// 26 | 27 | module diff_network ( 28 | input wire [6:0] i, 29 | output wire [6:0] o, 30 | input wire [6:0] k 31 | ); 32 | 33 | assign o[0] = k[0] ^ ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6]; 34 | assign o[1] = k[1] ^ i[0] ^ ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6]; 35 | assign o[2] = k[2] ^ i[0] ^ i[1] ^ ^ i[3] ^ i[4] ^ i[5] ^ i[6]; 36 | assign o[3] = k[3] ^ i[0] ^ i[1] ^ i[2] ^ ^ i[4] ^ i[5] ^ i[6]; 37 | assign o[4] = k[4] ^ i[0] ^ i[1] ^ i[2] ^ i[3] ^ ^ i[5] ^ i[6]; 38 | assign o[5] = k[5] ^ i[0] ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ ^ i[6]; 39 | assign o[6] = k[6] ^ i[0] ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6]; 40 | 41 | endmodule // diff_network 42 | -------------------------------------------------------------------------------- /firmware/bist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef CSR_GENERATOR_BASE 3 | #include "bist.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "ci.h" 12 | 13 | 14 | #define test_size 128*1024*1024 15 | 16 | unsigned int ticks; 17 | unsigned int speed; 18 | 19 | static void busy_wait(unsigned int ds) 20 | { 21 | timer0_en_write(0); 22 | timer0_reload_write(0); 23 | timer0_load_write(SYSTEM_CLOCK_FREQUENCY/10*ds); 24 | timer0_en_write(1); 25 | timer0_update_value_write(1); 26 | while(timer0_value_read()) timer0_update_value_write(1); 27 | } 28 | 29 | void bist_test(void) { 30 | // empty any characters pending 31 | while(readchar_nonblock() != 0) { 32 | printf( "readchar_nonblock(): %d\n", readchar_nonblock() ); 33 | printf( "emptying buffer: %02x\n", (unsigned int) uart_read() ); 34 | } 35 | while(readchar_nonblock() == 0) { 36 | // write 37 | printf("writing %d Mbytes...", test_size/(1024*1024)); 38 | generator_reset_write(1); 39 | generator_reset_write(0); 40 | generator_base_write(0x20000); 41 | generator_length_write((test_size*8)/128); 42 | 43 | timer0_en_write(0); 44 | timer0_load_write(0xffffffff); 45 | timer0_en_write(1); 46 | 47 | generator_start_write(1); 48 | while(generator_done_read() == 0); 49 | 50 | timer0_update_value_write(1); 51 | ticks = timer0_value_read(); 52 | ticks = 0xffffffff - ticks; 53 | speed = SYSTEM_CLOCK_FREQUENCY/ticks; 54 | speed = test_size*speed/1000000; 55 | speed = 8*speed; 56 | printf(" / %u Mbps\n", speed); 57 | 58 | // read 59 | printf("reading %d Mbytes...", test_size/(1024*1024)); 60 | checker_reset_write(1); 61 | checker_reset_write(0); 62 | checker_base_write(0x20000); 63 | checker_length_write((test_size*8)/128); 64 | 65 | timer0_en_write(0); 66 | timer0_load_write(0xffffffff); 67 | timer0_en_write(1); 68 | 69 | checker_start_write(1); 70 | while(checker_done_read() == 0); 71 | 72 | timer0_update_value_write(1); 73 | ticks = timer0_value_read(); 74 | ticks = 0xffffffff - ticks; 75 | speed = SYSTEM_CLOCK_FREQUENCY/ticks; 76 | speed = test_size*speed/1000000; 77 | speed = 8*speed; 78 | printf(" / %u Mbps\n", speed); 79 | 80 | // errors 81 | printf("errors: %d\n", checker_errors_read()); 82 | 83 | // delay 84 | busy_wait(10); 85 | } 86 | 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /firmware/hdmi_out0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef CSR_HDMI_OUT0_I2C_W_ADDR 3 | #include 4 | #include "i2c.h" 5 | #include "hdmi_out0.h" 6 | 7 | I2C hdmi_out0_i2c; 8 | int hdmi_out0_debug_enabled = 0; 9 | 10 | void hdmi_out0_i2c_init(void) { 11 | hdmi_out0_i2c.w_read = hdmi_out0_i2c_w_read; 12 | hdmi_out0_i2c.w_write = hdmi_out0_i2c_w_write; 13 | hdmi_out0_i2c.r_read = hdmi_out0_i2c_r_read; 14 | i2c_init(&hdmi_out0_i2c); 15 | } 16 | 17 | void hdmi_out0_print_edid(void) { 18 | int eeprom_addr, e, extension_number = 0; 19 | unsigned char b; 20 | unsigned char sum = 0; 21 | 22 | i2c_start_cond(&hdmi_out0_i2c); 23 | b = i2c_write(&hdmi_out0_i2c, 0xa0); 24 | if (!b && hdmi_out0_debug_enabled) 25 | printf("hdmi_out0: NACK while writing slave address!\r\n"); 26 | b = i2c_write(&hdmi_out0_i2c, 0x00); 27 | if (!b && hdmi_out0_debug_enabled) 28 | printf("hdmi_out0: NACK while writing eeprom address!\r\n"); 29 | i2c_start_cond(&hdmi_out0_i2c); 30 | b = i2c_write(&hdmi_out0_i2c, 0xa1); 31 | if (!b && hdmi_out0_debug_enabled) 32 | printf("hdmi_out0: NACK while writing slave address (2)!\r\n"); 33 | for (eeprom_addr = 0 ; eeprom_addr < 128 ; eeprom_addr++) { 34 | b = i2c_read(&hdmi_out0_i2c, eeprom_addr == 127 && extension_number == 0 ? 0 : 1); 35 | sum +=b; 36 | printf("%02X ", b); 37 | if(!((eeprom_addr+1) % 16)) 38 | printf("\r\n"); 39 | if(eeprom_addr == 126) 40 | extension_number = b; 41 | if(eeprom_addr == 127 && sum != 0) 42 | { 43 | printf("Checksum ERROR in EDID block 0\r\n"); 44 | i2c_stop_cond(&hdmi_out0_i2c); 45 | return; 46 | } 47 | } 48 | for(e = 0; e < extension_number; e++) 49 | { 50 | printf("\r\n"); 51 | sum = 0; 52 | for (eeprom_addr = 0 ; eeprom_addr < 128 ; eeprom_addr++) { 53 | b = i2c_read(&hdmi_out0_i2c, eeprom_addr == 127 && e == extension_number - 1 ? 0 : 1); 54 | sum += b; 55 | printf("%02X ", b); 56 | if(!((eeprom_addr+1) % 16)) 57 | printf("\r\n"); 58 | if(eeprom_addr == 127 && sum != 0) 59 | { 60 | printf("Checksum ERROR in EDID extension block %d\r\n", e); 61 | i2c_stop_cond(&hdmi_out0_i2c); 62 | return; 63 | } 64 | } 65 | } 66 | i2c_stop_cond(&hdmi_out0_i2c); 67 | } 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /firmware/i2c.c: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | #include "i2c.h" 3 | 4 | /* I2C bit banging */ 5 | int i2c_init(I2C *i2c) 6 | { 7 | unsigned int timeout; 8 | 9 | i2c->started = 0; 10 | i2c->w_write(I2C_SCL); 11 | /* Check the I2C bus is ready */ 12 | timeout = 1000; 13 | while((timeout > 0) && (!(i2c->r_read() & I2C_SDAIN))) timeout--; 14 | 15 | return timeout; 16 | } 17 | 18 | void i2c_delay(void) 19 | { 20 | unsigned int i; 21 | 22 | for(i=0;i<1000;i++) NOP; 23 | } 24 | 25 | /* I2C bit-banging functions from http://en.wikipedia.org/wiki/I2c */ 26 | unsigned int i2c_read_bit(I2C *i2c) 27 | { 28 | unsigned int bit; 29 | 30 | /* Let the slave drive data */ 31 | i2c->w_write(0); 32 | i2c_delay(); 33 | i2c->w_write(I2C_SCL); 34 | i2c_delay(); 35 | bit = i2c->r_read() & I2C_SDAIN; 36 | i2c_delay(); 37 | i2c->w_write(0); 38 | return bit; 39 | } 40 | 41 | void i2c_write_bit(I2C *i2c, unsigned int bit) 42 | { 43 | if(bit) { 44 | i2c->w_write(I2C_SDAOE | I2C_SDAOUT); 45 | } else { 46 | i2c->w_write(I2C_SDAOE); 47 | } 48 | i2c_delay(); 49 | /* Clock stretching */ 50 | i2c->w_write(i2c->w_read() | I2C_SCL); 51 | i2c_delay(); 52 | i2c->w_write(i2c->w_read() & ~I2C_SCL); 53 | } 54 | 55 | void i2c_start_cond(I2C *i2c) 56 | { 57 | if(i2c->started) { 58 | /* set SDA to 1 */ 59 | i2c->w_write(I2C_SDAOE | I2C_SDAOUT); 60 | i2c_delay(); 61 | i2c->w_write(i2c->w_read() | I2C_SCL); 62 | i2c_delay(); 63 | } 64 | /* SCL is high, set SDA from 1 to 0 */ 65 | i2c->w_write(I2C_SDAOE|I2C_SCL); 66 | i2c_delay(); 67 | i2c->w_write(I2C_SDAOE); 68 | i2c->started = 1; 69 | } 70 | 71 | void i2c_stop_cond(I2C *i2c) 72 | { 73 | /* set SDA to 0 */ 74 | i2c->w_write(I2C_SDAOE); 75 | i2c_delay(); 76 | /* Clock stretching */ 77 | i2c->w_write(I2C_SDAOE | I2C_SCL); 78 | /* SCL is high, set SDA from 0 to 1 */ 79 | i2c->w_write(I2C_SCL); 80 | i2c_delay(); 81 | i2c->started = 0; 82 | } 83 | 84 | unsigned int i2c_write(I2C *i2c, unsigned char byte) 85 | { 86 | unsigned int bit; 87 | unsigned int ack; 88 | 89 | for(bit = 0; bit < 8; bit++) { 90 | i2c_write_bit(i2c, byte & 0x80); 91 | byte <<= 1; 92 | } 93 | ack = !i2c_read_bit(i2c); 94 | return ack; 95 | } 96 | 97 | unsigned char i2c_read(I2C *i2c, int ack) 98 | { 99 | unsigned char byte = 0; 100 | unsigned int bit; 101 | 102 | for(bit = 0; bit < 8; bit++) { 103 | byte <<= 1; 104 | byte |= i2c_read_bit(i2c); 105 | } 106 | i2c_write_bit(i2c, !ack); 107 | return byte; 108 | } 109 | -------------------------------------------------------------------------------- /overlay/shuffle_network.v: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2011, Andrew "bunnie" Huang 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, 9 | // this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | // POSSIBILITY OF SUCH DAMAGE. 24 | // 25 | ////////////////////////////////////////////////////////////////////////////// 26 | 27 | module shuffle_network ( 28 | input wire clk, 29 | input wire reset, 30 | input wire din, 31 | input wire sel, 32 | input wire advance, 33 | input wire init_iv, 34 | output wire dout 35 | ); 36 | 37 | reg a, b; 38 | 39 | always @(posedge clk or posedge reset) begin 40 | if( reset == 1 ) begin 41 | a <= 1'b0; 42 | b <= 1'b1; 43 | end else begin 44 | if( init_iv ) begin 45 | a <= 1'b0; 46 | b <= 1'b1; 47 | end else if( advance ) begin 48 | a <= sel ? din : b; 49 | b <= sel ? a : din; 50 | end else begin 51 | a <= a; 52 | b <= b; 53 | end 54 | end // else: !if( reset == 1 ) 55 | end // always @ (posedge clk or posedge reset) 56 | 57 | assign dout = sel ? b : a; 58 | 59 | endmodule // shuffle_network 60 | -------------------------------------------------------------------------------- /overlay/DRAM16XN.v: -------------------------------------------------------------------------------- 1 | /* 2 | This program is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this program. If not, see . 14 | 15 | 16 | Copyright 2016 Andrew 'bunnie' Huang, all rights reserved 17 | */ 18 | // 19 | // Module: DRAM16XN 20 | // 21 | // Description: Distributed SelectRAM example 22 | // Dual Port 16 x N-bit 23 | // 24 | //--------------------------------------------------------------------------------------- 25 | 26 | module DRAM16XN #(parameter data_width = 20) 27 | ( 28 | DATA_IN, 29 | ADDRESS, 30 | ADDRESS_DP, 31 | WRITE_EN, 32 | CLK, 33 | O_DATA_OUT, 34 | O_DATA_OUT_DP); 35 | 36 | input [data_width-1:0]DATA_IN; 37 | input [3:0] ADDRESS; 38 | input [3:0] ADDRESS_DP; 39 | input WRITE_EN; 40 | input CLK; 41 | 42 | output [data_width-1:0]O_DATA_OUT_DP; 43 | output [data_width-1:0]O_DATA_OUT; 44 | 45 | genvar i; 46 | generate 47 | for(i = 0 ; i < data_width ; i = i + 1) begin : dram16s 48 | RAM16X1D i_RAM16X1D_U( 49 | .D(DATA_IN[i]), //insert input signal 50 | .WE(WRITE_EN), //insert Write Enable signal 51 | .WCLK(CLK), //insert Write Clock signal 52 | .A0(ADDRESS[0]), //insert Address 0 signal port SPO 53 | .A1(ADDRESS[1]), //insert Address 1 signal port SPO 54 | .A2(ADDRESS[2]), //insert Address 2 signal port SPO 55 | .A3(ADDRESS[3]), //insert Address 3 signal port SPO 56 | .DPRA0(ADDRESS_DP[0]), //insert Address 0 signal dual port DPO 57 | .DPRA1(ADDRESS_DP[1]), //insert Address 1 signal dual port DPO 58 | .DPRA2(ADDRESS_DP[2]), //insert Address 2 signal dual port DPO 59 | .DPRA3(ADDRESS_DP[3]), //insert Address 3 signal dual port DPO 60 | .SPO(O_DATA_OUT[i]), //insert output signal SPO 61 | .DPO(O_DATA_OUT_DP[i]) //insert output signal DPO 62 | ); 63 | end 64 | endgenerate 65 | 66 | endmodule 67 | 68 | -------------------------------------------------------------------------------- /firmware/dump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "stdio_wrap.h" 7 | 8 | #include 9 | #include 10 | 11 | #include "dump.h" 12 | 13 | /* General address space functions */ 14 | 15 | #define NUMBER_OF_BYTES_ON_A_LINE 16 16 | static void dump_bytes(unsigned int *ptr, int count, unsigned addr) 17 | { 18 | char *data = (char *)ptr; 19 | int line_bytes = 0, i = 0; 20 | 21 | putsnonl("Memory dump:"); 22 | while(count > 0){ 23 | line_bytes = 24 | (count > NUMBER_OF_BYTES_ON_A_LINE)? 25 | NUMBER_OF_BYTES_ON_A_LINE : count; 26 | 27 | wprintf("\n0x%08x ", addr); 28 | for(i=0;i 0x7e)) 38 | wprintf("."); 39 | else 40 | wprintf("%c", *(data+i)); 41 | } 42 | 43 | for(;i [length]\n"); 61 | return; 62 | } 63 | addr = (unsigned *)strtoul(startaddr, &c, 0); 64 | if(*c != 0) { 65 | wprintf("incorrect address\n"); 66 | return; 67 | } 68 | if(*len == 0) { 69 | length = 4; 70 | } else { 71 | length = strtoul(len, &c, 0); 72 | if(*c != 0) { 73 | wprintf("incorrect length\n"); 74 | return; 75 | } 76 | } 77 | 78 | dump_bytes(addr, length, (unsigned)addr); 79 | } 80 | 81 | void mw(char *addr, char *value, char *count) 82 | { 83 | char *c; 84 | unsigned int *addr2; 85 | unsigned int value2; 86 | unsigned int count2; 87 | unsigned int i; 88 | 89 | if((*addr == 0) || (*value == 0)) { 90 | wprintf("mw
[count]\n"); 91 | return; 92 | } 93 | addr2 = (unsigned int *)strtoul(addr, &c, 0); 94 | if(*c != 0) { 95 | wprintf("incorrect address\n"); 96 | return; 97 | } 98 | value2 = strtoul(value, &c, 0); 99 | if(*c != 0) { 100 | wprintf("incorrect value\n"); 101 | return; 102 | } 103 | if(*count == 0) { 104 | count2 = 1; 105 | } else { 106 | count2 = strtoul(count, &c, 0); 107 | if(*c != 0) { 108 | wprintf("incorrect count\n"); 109 | return; 110 | } 111 | } 112 | for (i=0;i [count]\n"); 125 | return; 126 | } 127 | dstaddr2 = (unsigned int *)strtoul(dstaddr, &c, 0); 128 | if(*c != 0) { 129 | wprintf("incorrect destination address\n"); 130 | return; 131 | } 132 | srcaddr2 = (unsigned int *)strtoul(srcaddr, &c, 0); 133 | if(*c != 0) { 134 | wprintf("incorrect source address\n"); 135 | return; 136 | } 137 | if(*count == 0) { 138 | count2 = 1; 139 | } else { 140 | count2 = strtoul(count, &c, 0); 141 | if(*c != 0) { 142 | wprintf("incorrect count\n"); 143 | return; 144 | } 145 | } 146 | for (i=0;i&1 | grep -E -o "tap/device found: [0-9a-z]+" | cut -f3 -d" "` 58 | 59 | printf "Got $IDCODE" 60 | 61 | if [ "$IDCODE" = "0x0362d093" ] 62 | then 63 | printf "Device type is 35T" 64 | DEVICE=35T 65 | elif [ "$IDCODE" = "0x13631093" ] 66 | then 67 | printf "Device type is 100T" 68 | DEVICE=100T 69 | else 70 | printf "Device type is unknown or invalid, can't update. Press return to exit.\n" 71 | read dummy 72 | exit 1 73 | fi 74 | 75 | 76 | if [ "$DEVICE" = "35T" ] 77 | then 78 | FPGAIMAGE="user35-$CABLE.bit" 79 | BSCANIMAGE="bscan_spi_xc7a35t.bit" 80 | elif [ "$DEVICE" = "100T" ] 81 | then 82 | FPGAIMAGE="user100-$CABLE.bit" 83 | BSCANIMAGE="bscan_spi_xc7a100t.bit" 84 | else 85 | printf "Device type not valid, aborting! Press return to exit.\n" 86 | read dummy 87 | exit 1 88 | fi 89 | 90 | # convert the firmware bin into an uploadable blob 91 | rm -f /tmp/ufirmware.upl 92 | rm -f /tmp/ufirmware.bin 93 | 94 | printf "\n\nPadding firmware image...\n" 95 | # pad the firmware out to fill out the full firmware area 96 | # the reason is that ufirmware.bin sometimes is not divisible by 4, which will 97 | # cause the CRC computation to fail. So this forces a padding on ufirmware.bin 98 | # which guarantees a deterministic fill for the entire firmware length and 99 | # thus allow CRC to succeed 100 | cp $GIT_DIR/testing-images/testing-firmware.bin /tmp/ufirmware.bin 101 | dd if=/dev/zero of=/tmp/ufirmware.bin bs=1 count=1 seek=131071 102 | $GIT_DIR/bin/mknetv2img -f --output /tmp/ufirmware.upl /tmp/ufirmware.bin 103 | if [ $? -ne 0 ] 104 | then 105 | printf "Could not pad firmware image, check permissions on /tmp. Press return to exit.\n" 106 | read dummy 107 | exit 1 108 | fi 109 | 110 | printf "\n\nBurning FPGA soft-core firmware update to SPINOR...\n" 111 | sudo openocd \ 112 | -c 'set FIRMWARE_FILE /tmp/ufirmware.upl' \ 113 | -c "set BSCAN_FILE $OCD_SCRIPT_DIR/$BSCANIMAGE" \ 114 | -f $OCD_SCRIPT_DIR/cl-firmware.cfg 115 | if [ $? -ne 0 ] 116 | then 117 | printf "Trouble with JTAG interface, aborting. Check for openocd and sudo priveledges. Press return to exit.\n" 118 | read dummy 119 | exit 1 120 | fi 121 | 122 | 123 | printf "\n\nBurning FPGA gateware bitstream to SPINOR (~1 minute)...\n" 124 | sudo openocd \ 125 | -c "set FPGAIMAGE $GIT_DIR/testing-images/$FPGAIMAGE" \ 126 | -c "set BSCAN_FILE $OCD_SCRIPT_DIR/$BSCANIMAGE" \ 127 | -f $OCD_SCRIPT_DIR/cl-spifpga.cfg 128 | if [ $? -ne 0 ] 129 | then 130 | printf "Trouble with JTAG interface, aborting. Check for openocd and sudo priveledges. Press return to exit.\n" 131 | read dummy 132 | exit 1 133 | fi 134 | 135 | printf "\n\nFPGA update to testing version complete. Press return to exit.\n" 136 | read dummy 137 | 138 | exit 0 139 | -------------------------------------------------------------------------------- /sim/sim_pulsesync.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import lxbuildenv_sim 4 | 5 | # This variable defines all the external programs that this module 6 | # relies on. lxbuildenv reads this variable in order to ensure 7 | # the build will finish without exiting due to missing third-party 8 | # programs. 9 | LX_DEPENDENCIES = ["riscv", "vivado"] 10 | 11 | import sys 12 | 13 | from migen import * 14 | 15 | from litex.build.generic_platform import * 16 | from litex.build.xilinx import XilinxPlatform 17 | 18 | from litex.soc.integration.builder import * 19 | from litex.soc.cores.clock import * 20 | 21 | from migen.genlib.cdc import PulseSynchronizer 22 | 23 | sim_config = { 24 | # freqs 25 | "input_clk_freq": 50e6, 26 | "sys_clk_freq": 100e6, 27 | "adc_clk_freq": 20e6, 28 | } 29 | 30 | 31 | _io = [ 32 | ("clk", 0, Pins("X")), 33 | ("rst", 0, Pins("X")), 34 | ] 35 | 36 | 37 | class Platform(XilinxPlatform): 38 | def __init__(self): 39 | XilinxPlatform.__init__(self, "", _io) 40 | 41 | 42 | class CRG(Module): 43 | def __init__(self, platform, core_config): 44 | # build a simulated PLL. You can add more pll.create_clkout() lines to add more clock frequencies as necessary 45 | self.clock_domains.cd_sys = ClockDomain() 46 | self.clock_domains.cd_adc = ClockDomain() 47 | 48 | self.submodules.pll = pll = S7MMCM() 49 | self.comb += pll.reset.eq(platform.request("rst")) 50 | pll.register_clkin(platform.request("clk"), sim_config["input_clk_freq"]) 51 | pll.create_clkout(self.cd_sys, sim_config["sys_clk_freq"]) 52 | pll.create_clkout(self.cd_adc, sim_config["adc_clk_freq"]) 53 | 54 | 55 | pulse_vect = [0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0] 56 | 57 | class SimpleSim(Module): 58 | def __init__(self, platform): 59 | # instantiate the clock module 60 | crg = CRG(platform, sim_config) 61 | self.submodules += crg 62 | 63 | 64 | # instantiate the DUT 65 | pulse_in = Signal() 66 | pulse_out = Signal() 67 | self.submodules.pulsesync = PulseSynchronizer("sys", "adc") 68 | self.comb += self.pulsesync.i.eq(pulse_in) 69 | self.comb += pulse_out.eq(self.pulsesync.o) 70 | 71 | # connect test vectors to DUT 72 | mult = Signal(4) # allow each element in pulse_vect to be repeated "mult" times 73 | count = Signal(4) # counter variable to implement mult 74 | self.comb += mult.eq(5) # in this case, 5 75 | 76 | index = Signal(4) # tracks the index of the vector we are in 77 | self.sync.sys += [ # generate vector index based on count/multiply params 78 | count.eq(count + 1), 79 | If(count == mult, 80 | index.eq(index + 1), 81 | count.eq(0), 82 | ) 83 | ] 84 | # now assign vector to DUT signal(s) 85 | for k, i in enumerate(pulse_vect): 86 | self.sync.sys += [ 87 | If( (count == 0) & (k == index), 88 | pulse_in.eq(i) 89 | ) 90 | ] 91 | 92 | def generate_top(): 93 | platform = Platform() 94 | soc = SimpleSim(platform) 95 | platform.build(soc, build_dir="./run", run=False) # run=False prevents synthesis from happening, but a top.v file gets kicked out 96 | 97 | 98 | # this generates a test bench wrapper verilog file, needed by the xilinx tools 99 | def generate_top_tb(): 100 | f = open("run/top_tb.v", "w") 101 | f.write(""" 102 | `timescale 1ns/1ps 103 | 104 | module top_tb(); 105 | 106 | reg clk; 107 | initial clk = 1'b1; 108 | always #10 clk = ~clk; 109 | 110 | top dut ( 111 | .clk(clk), 112 | .rst(0) 113 | ); 114 | 115 | endmodule""") 116 | f.close() 117 | 118 | 119 | # this ties it all together 120 | def run_sim(gui=False): 121 | os.system("mkdir -p run") 122 | os.system("rm -rf run/xsim.dir") 123 | if sys.platform == "win32": 124 | call_cmd = "call " 125 | else: 126 | call_cmd = "" 127 | os.system(call_cmd + "cd run && xvlog ../glbl.v") 128 | os.system(call_cmd + "cd run && xvlog top.v -sv") 129 | os.system(call_cmd + "cd run && xvlog top_tb.v -sv ") 130 | os.system(call_cmd + "cd run && xelab -debug typical top_tb glbl -s top_tb_sim -L unisims_ver -L unimacro_ver -L SIMPRIM_VER -L secureip -L $xsimdir/xil_defaultlib -timescale 1ns/1ps") 131 | if gui: 132 | os.system(call_cmd + "cd run && xsim top_tb_sim -gui") 133 | else: 134 | os.system(call_cmd + "cd run && xsim top_tb_sim -runall") 135 | 136 | 137 | def main(): 138 | generate_top() 139 | generate_top_tb() 140 | run_sim(gui=True) 141 | 142 | 143 | if __name__ == "__main__": 144 | main() 145 | -------------------------------------------------------------------------------- /software/pcie/user/litepcie_lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * LitePCIe library 3 | * 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "litepcie.h" 17 | #include "cutils.h" 18 | #include "config.h" 19 | #include "csr.h" 20 | #include "flags.h" 21 | 22 | #include "litepcie_lib.h" 23 | 24 | /* 25 | TODO: 26 | - DMA overflow/underflow detection 27 | */ 28 | 29 | void *litepcie_malloc(int size) 30 | { 31 | return malloc(size); 32 | } 33 | 34 | void *litepcie_mallocz(int size) 35 | { 36 | void *ptr; 37 | ptr = litepcie_malloc(size); 38 | if (!ptr) 39 | return NULL; 40 | memset(ptr, 0, size); 41 | return ptr; 42 | } 43 | 44 | void litepcie_free(void *ptr) 45 | { 46 | free(ptr); 47 | } 48 | 49 | void __attribute__((format(printf, 2, 3))) litepcie_log(LitePCIeState *s, const char *fmt, ...) 50 | { 51 | va_list ap; 52 | 53 | va_start(ap, fmt); 54 | vfprintf(stderr, fmt, ap); 55 | va_end(ap); 56 | } 57 | 58 | /* in ms */ 59 | int64_t litepcie_get_time_ms(void) 60 | { 61 | struct timespec ts; 62 | clock_gettime(CLOCK_MONOTONIC, &ts); 63 | return (int64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000U); 64 | } 65 | 66 | LitePCIeState *litepcie_open(const char *device_name) 67 | { 68 | LitePCIeState *s; 69 | 70 | s = litepcie_mallocz(sizeof(LitePCIeState)); 71 | if (!s) 72 | return NULL; 73 | 74 | s->litepcie_fd = open(device_name, O_RDWR); 75 | if (s->litepcie_fd < 0) { 76 | perror(device_name); 77 | goto fail; 78 | } 79 | 80 | /* map the DMA buffers */ 81 | if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_GET_MMAP_INFO, &s->mmap_info) != 0) { 82 | perror("LITEPCIE_IOCTL_GET_MMAP_INFO"); 83 | exit(1); 84 | } 85 | 86 | s->dma_tx_buf = mmap(NULL, s->mmap_info.dma_tx_buf_size * 87 | s->mmap_info.dma_tx_buf_count, 88 | PROT_READ | PROT_WRITE, MAP_SHARED, s->litepcie_fd, 89 | s->mmap_info.dma_tx_buf_offset); 90 | if (s->dma_tx_buf == MAP_FAILED) { 91 | perror("mmap1"); 92 | exit(1); 93 | } 94 | 95 | s->dma_rx_buf = mmap(NULL, s->mmap_info.dma_rx_buf_size * 96 | s->mmap_info.dma_rx_buf_count, 97 | PROT_READ | PROT_WRITE, MAP_SHARED, s->litepcie_fd, 98 | s->mmap_info.dma_rx_buf_offset); 99 | if (s->dma_rx_buf == MAP_FAILED) { 100 | perror("mmap2"); 101 | exit(1); 102 | } 103 | 104 | /* map the registers */ 105 | s->reg_buf = mmap(NULL, s->mmap_info.reg_size, 106 | PROT_READ | PROT_WRITE, MAP_SHARED, s->litepcie_fd, 107 | s->mmap_info.reg_offset); 108 | if (s->reg_buf == MAP_FAILED) { 109 | perror("mmap2"); 110 | exit(1); 111 | } 112 | 113 | s->dma_tx_buf_size = s->mmap_info.dma_tx_buf_size; 114 | s->dma_rx_buf_size = s->mmap_info.dma_rx_buf_size; 115 | 116 | pthread_mutex_init(&s->fifo_mutex, NULL); 117 | 118 | return s; 119 | fail: 120 | litepcie_close(s); 121 | return NULL; 122 | } 123 | 124 | void litepcie_dma_start(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback) 125 | { 126 | struct litepcie_ioctl_dma_start dma_start; 127 | 128 | if (buf_count > DMA_BUFFER_COUNT) { 129 | litepcie_log(s, "unsupported buf_count\n"); 130 | exit(1); 131 | } 132 | 133 | s->tx_buf_size = s->rx_buf_size = buf_size; 134 | s->tx_buf_count = s->rx_buf_count = buf_count; 135 | 136 | dma_start.dma_flags = 0; 137 | if (is_loopback) 138 | dma_start.dma_flags |= DMA_LOOPBACK_ENABLE; 139 | dma_start.tx_buf_size = s->tx_buf_size; 140 | dma_start.tx_buf_count = s->tx_buf_count; 141 | dma_start.rx_buf_size = s->rx_buf_size; 142 | dma_start.rx_buf_count = s->rx_buf_count; 143 | if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_START, &dma_start) < 0) { 144 | perror("LITEPCIE_IOCTL_DMA_START"); 145 | } 146 | } 147 | 148 | void litepcie_dma_stop(LitePCIeState *s) 149 | { 150 | if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_STOP, NULL) < 0) { 151 | perror("LITEPCIE_IOCTL_DMA_STOP"); 152 | } 153 | } 154 | 155 | void litepcie_writel(LitePCIeState *s, uint32_t addr, uint32_t val) 156 | { 157 | *(volatile uint32_t *)(s->reg_buf + addr) = val; 158 | } 159 | 160 | uint32_t litepcie_readl(LitePCIeState *s, uint32_t addr) 161 | { 162 | return *(volatile uint32_t *)(s->reg_buf + addr); 163 | } 164 | 165 | void litepcie_close(LitePCIeState *s) 166 | { 167 | pthread_mutex_destroy(&s->fifo_mutex); 168 | 169 | if (s->dma_tx_buf) { 170 | munmap(s->dma_tx_buf, s->mmap_info.dma_tx_buf_size * 171 | s->mmap_info.dma_tx_buf_count); 172 | } 173 | if (s->dma_rx_buf) { 174 | munmap(s->dma_rx_buf, s->mmap_info.dma_rx_buf_size * 175 | s->mmap_info.dma_rx_buf_count); 176 | } 177 | if (s->reg_buf) 178 | munmap(s->reg_buf, s->mmap_info.reg_size); 179 | if (s->litepcie_fd >= 0) 180 | close(s->litepcie_fd); 181 | litepcie_free(s); 182 | } 183 | -------------------------------------------------------------------------------- /firmware/km.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "km.h" 8 | 9 | #define SOURCE 1 10 | #define SINK 0 11 | 12 | unsigned char read_hdcp(unsigned char addr) { 13 | i2c_snoop_edid_snoop_adr_write( addr ); 14 | return( i2c_snoop_edid_snoop_dat_read() ); 15 | } 16 | 17 | #define CHECK_KM 0x86a4df6560c88eLL // panasonic + LG 18 | //#define CHECK_KM 0x225d10cee24175LL // appleTV + LG 19 | #define CHECK 0 20 | 21 | #define DEBUG 0 22 | 23 | int link_redo = 0; 24 | 25 | void hdcp_init(void) { 26 | // unmask the interrupts for HDCP 27 | unsigned int mask; 28 | mask = irq_getmask(); 29 | mask |= 1 << HDCP_INTERRUPT; 30 | irq_setmask(mask); 31 | wprintf("interrupt mask (hdcp): %x\n", mask); 32 | 33 | hdcp_ev_enable_write(1); 34 | wprintf("hdcp_ev_enable_read: %d\n", hdcp_ev_enable_read()); 35 | 36 | // hdcp_Aksv_mode_write(0); 37 | hdcp_Aksv_manual_write(0); // clear to zero for default, as it's rising-edge triggered 38 | hdcp_Aksv_mode_write(1); // select manual Aksv mode 39 | } 40 | 41 | void hdcp_isr(void) { 42 | unsigned int stat; 43 | 44 | hdcp_ev_pending_write(1); 45 | 46 | if( derive_km() ) { 47 | link_redo = 1; 48 | } 49 | hdcp_Aksv_manual_write(1); 50 | 51 | hdcp_ev_enable_write(1); 52 | 53 | printf( "Km: %08x %08x\n", (unsigned long) (hdcp_Km_read() >> 32), (unsigned long) hdcp_Km_read() ); 54 | hdcp_Aksv_manual_write(0); // clear for next use: this is rising edge-triggered 55 | 56 | } 57 | 58 | int derive_km(void) { 59 | unsigned int num; 60 | int i; 61 | 62 | unsigned long long source_ksv = 0LL; 63 | unsigned long long sink_ksv = 0LL; 64 | unsigned long long ksv_temp = 0LL; 65 | unsigned long long Km = 0LL; 66 | unsigned long long Kmp = 0LL; 67 | 68 | unsigned long long source_pkey[40]; 69 | unsigned long long sink_pkey[40]; 70 | 71 | hdcp_Km_valid_write(0); 72 | for( i = 0; i < 5; i++ ) { 73 | sink_ksv <<= 8; 74 | sink_ksv |= (read_hdcp(4 - i) & 0xff); 75 | } 76 | 77 | for( i = 0; i < 5; i++ ) { 78 | source_ksv <<= 8; 79 | source_ksv |= (read_hdcp(4 - i + 0x10) & 0xff); 80 | } 81 | 82 | compute_keys( (unsigned long) (source_ksv >> 32), (unsigned long) source_ksv, SOURCE, source_pkey ); 83 | compute_keys( (unsigned long) (sink_ksv >> 32), (unsigned long) sink_ksv, SINK, sink_pkey ); 84 | #if DEBUG 85 | wprintf( "source public ksv (lsb): %08x\n", (unsigned long) source_ksv ); 86 | wprintf( "source public ksv (msb): %08x\n", (unsigned long) (source_ksv >> 32) ); 87 | wprintf( "sink public ksv (lsb): %08x\n", (unsigned long) sink_ksv ); 88 | wprintf( "sink public ksv (msb): %08x\n", (unsigned long) (sink_ksv >> 32) ); 89 | #endif 90 | 91 | ksv_temp = source_ksv; // source Ksv 92 | num = 0; 93 | for( i = 0; i < 40; i++ ) { 94 | if( ksv_temp & 1LL ) { 95 | num++; 96 | Km += sink_pkey[i]; // used to select sink's keys 97 | Km &= 0xFFFFFFFFFFFFFFLL; 98 | // Km %= 72057594037927936LL; 99 | // wprintf( "Km 0x%08x", (unsigned long) (Km >> 32) ); 100 | // wprintf( "%08x\n", (unsigned long) Km ); 101 | } 102 | ksv_temp >>= 1LL; 103 | } 104 | // printf( "num 1's: %d\n", num ); 105 | // km is the sink km 106 | 107 | ksv_temp = sink_ksv; // sink Ksv 108 | num = 0; 109 | for( i = 0; i < 40; i++ ) { 110 | if( ksv_temp & 1LL ) { 111 | num++; 112 | Kmp += source_pkey[i]; // used to select source's keys 113 | Kmp &= 0xFFFFFFFFFFFFFFLL; 114 | // Kmp %= 72057594037927936LL; 115 | // printf( "Kmp %014llx\n", Kmp ); 116 | } 117 | ksv_temp >>= 1LL; 118 | } 119 | // printf( "num 1's: %d\n", num ); 120 | // Kmp is the source Km 121 | 122 | Km &= 0xFFFFFFFFFFFFFFLL; 123 | Kmp &= 0xFFFFFFFFFFFFFFLL; 124 | 125 | // wprintf( "\n" ); 126 | #if DEBUG 127 | wprintf( "Km (lsb): %08x\n", (unsigned long) (Km & 0xFFFFFFFF) ); 128 | wprintf( "Km (msb): %08x\n", (unsigned long) (Km >> 32) ); 129 | wprintf( "Km'(lsb): %08x\n", (unsigned long) (Kmp & 0xFFFFFFFF) ); 130 | wprintf( "Km'(msb): %08x\n", (unsigned long) (Kmp >> 32) ); 131 | #endif 132 | 133 | if( Km != Kmp ) { 134 | wprintf( "Km is not equal to Km', can't encrypt this stream.\n" ); 135 | return 1; 136 | } 137 | 138 | if( Km == 0 ) { 139 | wprintf( "Km is zero. This probably means derive_km was fired spuriously on disconnect.\n" ); 140 | wprintf( "Aborting without doing anything, since Km = 0 is never a correct condition\n" ); 141 | return 1; 142 | } else { 143 | #if DEBUG 144 | wprintf( "Committing Km\n" ); 145 | #endif 146 | // now commit Km to the fpga 147 | // if( Km != CHECK_KM ) { 148 | // wprintf( "*****Km doesn't match check value*****\n" ); 149 | // wprintf( "Writing check Km instead\n" ); 150 | // Km = CHECK_KM; 151 | // } 152 | 153 | unsigned char foo; 154 | for( i = 6; i >= 0; i-- ) { 155 | // start with the LSB, which gets written to to the highest CSR address 156 | foo = (unsigned char)(Km & 0xFF); 157 | #if DEBUG 158 | wprintf( "Writing to %02x to %08x\n", foo, CSR_HDCP_BASE + i * 4); 159 | #endif 160 | MMPTR(CSR_HDCP_BASE + i * 4) = foo; 161 | Km >>= 8; 162 | } 163 | 164 | #if DEBUG 165 | wprintf( "Confirm check Km as writ: (LSB) %08x\n", (unsigned long) hdcp_Km_read() ); 166 | wprintf( "Confirm check Km as writ: (MSB) %08x\n", (unsigned long) (hdcp_Km_read() >> 32) ); 167 | #endif 168 | // for( i = 0; i < 7; i++ ) { 169 | // write_km( i, Km & 0xFF ); 170 | // Km >>= 8; 171 | // } 172 | 173 | #if DEBUG 174 | printf( "Flipping Km valid\n" ); 175 | #endif 176 | // indicate Km ready 177 | hdcp_Km_valid_write(1); 178 | //write_km( 7, 1 ); 179 | } 180 | 181 | #if 0 182 | int last_event; 183 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 184 | ; 185 | } 186 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 187 | ; 188 | } 189 | wprintf( "Invoking HPD\n" ); 190 | hdcp_hpd_ena_write(1); 191 | elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY); 192 | 193 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 194 | ; 195 | } 196 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 197 | ; 198 | } 199 | wprintf( "Releasing HPD\n" ); 200 | hdcp_hpd_ena_write(0); 201 | #endif 202 | 203 | return 0; 204 | } 205 | -------------------------------------------------------------------------------- /firmware/pattern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include "flags.h" 7 | #include 8 | #include 9 | 10 | #include "pattern.h" 11 | #include "processor.h" 12 | 13 | #define PATTERN_FRAMEBUFFER_BASE 0x08000000 + 0x100000 14 | 15 | unsigned int pattern_framebuffer_base(void) { 16 | return PATTERN_FRAMEBUFFER_BASE; 17 | } 18 | 19 | static const unsigned int color_bar[8] = { 20 | YCBCR422_WHITE, 21 | YCBCR422_YELLOW, 22 | YCBCR422_CYAN, 23 | YCBCR422_GREEN, 24 | YCBCR422_PURPLE, 25 | YCBCR422_RED, 26 | YCBCR422_BLUE, 27 | YCBCR422_BLACK 28 | }; 29 | 30 | static const unsigned char font5x7[] = { 31 | 0x00, 0x00, 0x00, 0x00, 0x00,// (space) 32 | 0x00, 0x00, 0x5F, 0x00, 0x00,// ! 33 | 0x00, 0x07, 0x00, 0x07, 0x00,// " 34 | 0x14, 0x7F, 0x14, 0x7F, 0x14,// # 35 | 0x24, 0x2A, 0x7F, 0x2A, 0x12,// $ 36 | 0x23, 0x13, 0x08, 0x64, 0x62,// % 37 | 0x36, 0x49, 0x55, 0x22, 0x50,// & 38 | 0x00, 0x05, 0x03, 0x00, 0x00,// ' 39 | 0x00, 0x1C, 0x22, 0x41, 0x00,// ( 40 | 0x00, 0x41, 0x22, 0x1C, 0x00,// ) 41 | 0x08, 0x2A, 0x1C, 0x2A, 0x08,// * 42 | 0x08, 0x08, 0x3E, 0x08, 0x08,// + 43 | 0x00, 0x50, 0x30, 0x00, 0x00,// , 44 | 0x08, 0x08, 0x08, 0x08, 0x08,// - 45 | 0x00, 0x60, 0x60, 0x00, 0x00,// . 46 | 0x20, 0x10, 0x08, 0x04, 0x02,// / 47 | 0x3E, 0x51, 0x49, 0x45, 0x3E,// 0 48 | 0x00, 0x42, 0x7F, 0x40, 0x00,// 1 49 | 0x42, 0x61, 0x51, 0x49, 0x46,// 2 50 | 0x21, 0x41, 0x45, 0x4B, 0x31,// 3 51 | 0x18, 0x14, 0x12, 0x7F, 0x10,// 4 52 | 0x27, 0x45, 0x45, 0x45, 0x39,// 5 53 | 0x3C, 0x4A, 0x49, 0x49, 0x30,// 6 54 | 0x01, 0x71, 0x09, 0x05, 0x03,// 7 55 | 0x36, 0x49, 0x49, 0x49, 0x36,// 8 56 | 0x06, 0x49, 0x49, 0x29, 0x1E,// 9 57 | 0x00, 0x36, 0x36, 0x00, 0x00,// : 58 | 0x00, 0x56, 0x36, 0x00, 0x00,// ; 59 | 0x00, 0x08, 0x14, 0x22, 0x41,// < 60 | 0x14, 0x14, 0x14, 0x14, 0x14,// = 61 | 0x41, 0x22, 0x14, 0x08, 0x00,// > 62 | 0x02, 0x01, 0x51, 0x09, 0x06,// ? 63 | 0x32, 0x49, 0x79, 0x41, 0x3E,// @ 64 | 0x7E, 0x11, 0x11, 0x11, 0x7E,// A 65 | 0x7F, 0x49, 0x49, 0x49, 0x36,// B 66 | 0x3E, 0x41, 0x41, 0x41, 0x22,// C 67 | 0x7F, 0x41, 0x41, 0x22, 0x1C,// D 68 | 0x7F, 0x49, 0x49, 0x49, 0x41,// E 69 | 0x7F, 0x09, 0x09, 0x01, 0x01,// F 70 | 0x3E, 0x41, 0x41, 0x51, 0x32,// G 71 | 0x7F, 0x08, 0x08, 0x08, 0x7F,// H 72 | 0x00, 0x41, 0x7F, 0x41, 0x00,// I 73 | 0x20, 0x40, 0x41, 0x3F, 0x01,// J 74 | 0x7F, 0x08, 0x14, 0x22, 0x41,// K 75 | 0x7F, 0x40, 0x40, 0x40, 0x40,// L 76 | 0x7F, 0x02, 0x04, 0x02, 0x7F,// M 77 | 0x7F, 0x04, 0x08, 0x10, 0x7F,// N 78 | 0x3E, 0x41, 0x41, 0x41, 0x3E,// O 79 | 0x7F, 0x09, 0x09, 0x09, 0x06,// P 80 | 0x3E, 0x41, 0x51, 0x21, 0x5E,// Q 81 | 0x7F, 0x09, 0x19, 0x29, 0x46,// R 82 | 0x46, 0x49, 0x49, 0x49, 0x31,// S 83 | 0x01, 0x01, 0x7F, 0x01, 0x01,// T 84 | 0x3F, 0x40, 0x40, 0x40, 0x3F,// U 85 | 0x1F, 0x20, 0x40, 0x20, 0x1F,// V 86 | 0x7F, 0x20, 0x18, 0x20, 0x7F,// W 87 | 0x63, 0x14, 0x08, 0x14, 0x63,// X 88 | 0x03, 0x04, 0x78, 0x04, 0x03,// Y 89 | 0x61, 0x51, 0x49, 0x45, 0x43,// Z 90 | 0x00, 0x00, 0x7F, 0x41, 0x41,// [ 91 | 0x02, 0x04, 0x08, 0x10, 0x20,// "\" 92 | 0x41, 0x41, 0x7F, 0x00, 0x00,// ] 93 | 0x04, 0x02, 0x01, 0x02, 0x04,// ^ 94 | 0x40, 0x40, 0x40, 0x40, 0x40,// _ 95 | 0x00, 0x01, 0x02, 0x04, 0x00,// ` 96 | 0x20, 0x54, 0x54, 0x54, 0x78,// a 97 | 0x7F, 0x48, 0x44, 0x44, 0x38,// b 98 | 0x38, 0x44, 0x44, 0x44, 0x20,// c 99 | 0x38, 0x44, 0x44, 0x48, 0x7F,// d 100 | 0x38, 0x54, 0x54, 0x54, 0x18,// e 101 | 0x08, 0x7E, 0x09, 0x01, 0x02,// f 102 | 0x08, 0x14, 0x54, 0x54, 0x3C,// g 103 | 0x7F, 0x08, 0x04, 0x04, 0x78,// h 104 | 0x00, 0x44, 0x7D, 0x40, 0x00,// i 105 | 0x20, 0x40, 0x44, 0x3D, 0x00,// j 106 | 0x00, 0x7F, 0x10, 0x28, 0x44,// k 107 | 0x00, 0x41, 0x7F, 0x40, 0x00,// l 108 | 0x7C, 0x04, 0x18, 0x04, 0x78,// m 109 | 0x7C, 0x08, 0x04, 0x04, 0x78,// n 110 | 0x38, 0x44, 0x44, 0x44, 0x38,// o 111 | 0x7C, 0x14, 0x14, 0x14, 0x08,// p 112 | 0x08, 0x14, 0x14, 0x18, 0x7C,// q 113 | 0x7C, 0x08, 0x04, 0x04, 0x08,// r 114 | 0x48, 0x54, 0x54, 0x54, 0x20,// s 115 | 0x04, 0x3F, 0x44, 0x40, 0x20,// t 116 | 0x3C, 0x40, 0x40, 0x20, 0x7C,// u 117 | 0x1C, 0x20, 0x40, 0x20, 0x1C,// v 118 | 0x3C, 0x40, 0x30, 0x40, 0x3C,// w 119 | 0x44, 0x28, 0x10, 0x28, 0x44,// x 120 | 0x0C, 0x50, 0x50, 0x50, 0x3C,// y 121 | 0x44, 0x64, 0x54, 0x4C, 0x44,// z 122 | 0x00, 0x08, 0x36, 0x41, 0x00,// { 123 | 0x00, 0x00, 0x7F, 0x00, 0x00,// | 124 | 0x00, 0x41, 0x36, 0x08, 0x00,// } 125 | 0x08, 0x08, 0x2A, 0x1C, 0x08,// -> 126 | 0x08, 0x1C, 0x2A, 0x08, 0x08 // <- 127 | }; 128 | 129 | static int inc_color(int color) { 130 | color++; 131 | return color%8; 132 | } 133 | 134 | static void pattern_draw_text(int x, int y, char *ptr) { 135 | int i, j, k; 136 | int adr; 137 | volatile unsigned int *framebuffer = (unsigned int *)(MAIN_RAM_BASE + PATTERN_FRAMEBUFFER_BASE); 138 | for(i=0; ptr[i] != '\0'; i++) { 139 | for(j=0; j<7; j++) { 140 | for(k=0; k<5; k++) { 141 | adr = 5*(x + i) + k + (2*8*y + 2*j)*processor_h_active/2; 142 | if((font5x7[5*(ptr[i] - ' ') + k] >> j) & 0x1) { 143 | framebuffer[adr + 0*processor_h_active/2] = YCBCR422_BLACK; 144 | framebuffer[adr + 1*processor_h_active/2] = YCBCR422_BLACK; 145 | } 146 | else { 147 | framebuffer[adr + 0*processor_h_active/2] = YCBCR422_WHITE; 148 | framebuffer[adr + 1*processor_h_active/2] = YCBCR422_WHITE; 149 | } 150 | } 151 | } 152 | } 153 | } 154 | 155 | void pattern_fill_framebuffer(int h_active, int m_active) 156 | { 157 | int i; 158 | int color; 159 | flush_l2_cache(); 160 | color = -1; 161 | volatile unsigned int *framebuffer = (unsigned int *)(MAIN_RAM_BASE + PATTERN_FRAMEBUFFER_BASE); 162 | if (pattern == COLOR_BAR_PATTERN) { 163 | /* color bar pattern */ 164 | for(i=0; i= 0) 168 | framebuffer[i] = color_bar[color]; 169 | } 170 | } else if (pattern == BLACK_WHITE_BAR_PATTERN) { 171 | /* vertical black white lines */ 172 | for(i=0; i 2 | #include 3 | #ifdef ENCODER_BASE 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "processor.h" 10 | #include "encoder.h" 11 | 12 | void encoder_write_reg(unsigned int adr, unsigned int value) { 13 | MMPTR(ENCODER_BASE+adr) = value; 14 | } 15 | 16 | unsigned int encoder_read_reg(unsigned int adr) { 17 | return MMPTR(ENCODER_BASE+adr); 18 | } 19 | 20 | const char luma_rom_100[64] = { 21 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 22 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 23 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 24 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 25 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 26 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 27 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 28 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 29 | }; 30 | 31 | const char luma_rom_85[64] = { 32 | 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 33 | 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C, 34 | 0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B, 35 | 0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F, 36 | 0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 37 | 0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 38 | 0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 39 | 0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E 40 | }; 41 | 42 | const char luma_rom_75[64] = { 43 | 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 44 | 0x07, 0x07, 0x09, 0x09, 0x08, 0x0A, 0x0C, 0x14, 45 | 0x0D, 0x0C, 0x0B, 0x0B, 0x0C, 0x19, 0x12, 0x13, 46 | 0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D, 0x1A, 47 | 0x1C, 0x1C, 0x20, 0x24, 0x2E, 0x27, 0x20, 0x22, 48 | 0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29, 0x2C, 49 | 0x30, 0x31, 0x34, 0x34, 0x34, 0x1F, 0x27, 0x39, 50 | 0x3D, 0x38, 0x32, 0x3C, 0x2E, 0x33, 0x34, 0x32 51 | }; 52 | 53 | const char luma_rom_50[64] = { 54 | 0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E, 55 | 0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 56 | 0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 57 | 0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33, 58 | 0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 59 | 0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 60 | 0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 61 | 0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63 62 | }; 63 | 64 | const char chroma_rom_100[64] = { 65 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 66 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 67 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 68 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 69 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 70 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 71 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 72 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 73 | }; 74 | 75 | const char chroma_rom_85[64] = { 76 | 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 77 | 0x07, 0x07, 0x09, 0x09, 0x08, 0x0A, 0x0C, 0x14, 78 | 0x0D, 0x0C, 0x0B, 0x0B, 0x0C, 0x19, 0x12, 0x13, 79 | 0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D, 0x1A, 80 | 0x1C, 0x1C, 0x20, 0x24, 0x2E, 0x27, 0x20, 0x22, 81 | 0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29, 0x2C, 82 | 0x30, 0x31, 0x34, 0x34, 0x34, 0x1F, 0x27, 0x39, 83 | 0x3D, 0x38, 0x32, 0x3C, 0x2E, 0x33, 0x34, 0x32 84 | }; 85 | 86 | const char chroma_rom_75[64] = { 87 | 0x09, 0x09, 0x09, 0x0C, 0x0B, 0x0C, 0x18, 0x0D, 88 | 0x0D, 0x18, 0x32, 0x21, 0x1C, 0x21, 0x32, 0x32, 89 | 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 90 | 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 91 | 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 92 | 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 93 | 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 94 | 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32 95 | }; 96 | 97 | const char chroma_rom_50[64] = { 98 | 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 99 | 0x1A, 0x2F, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 100 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 101 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 102 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 103 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 104 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 105 | 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 106 | }; 107 | 108 | static void encoder_config_table(unsigned int base, const char *table) 109 | { 110 | int i; 111 | for(i=0; i<64; i++) 112 | encoder_write_reg(base+4*i, table[i]); 113 | } 114 | 115 | void encoder_init(int quality) { 116 | if(quality == 100) { 117 | encoder_config_table(ENCODER_QUANTIZER_RAM_LUMA_BASE, luma_rom_100); 118 | encoder_config_table(ENCODER_QUANTIZER_RAM_CHROMA_BASE, chroma_rom_100); 119 | encoder_quality = 100; 120 | } else if (quality == 85) { 121 | encoder_config_table(ENCODER_QUANTIZER_RAM_LUMA_BASE, luma_rom_85); 122 | encoder_config_table(ENCODER_QUANTIZER_RAM_CHROMA_BASE, chroma_rom_85); 123 | encoder_quality = 85; 124 | } else if (quality == 75) { 125 | encoder_config_table(ENCODER_QUANTIZER_RAM_LUMA_BASE, luma_rom_75); 126 | encoder_config_table(ENCODER_QUANTIZER_RAM_CHROMA_BASE, chroma_rom_75); 127 | encoder_quality = 75; 128 | } else { 129 | encoder_config_table(ENCODER_QUANTIZER_RAM_LUMA_BASE, luma_rom_50); 130 | encoder_config_table(ENCODER_QUANTIZER_RAM_CHROMA_BASE, chroma_rom_50); 131 | encoder_quality = 50; 132 | } 133 | } 134 | 135 | void encoder_start(short resx, short resy) { 136 | encoder_write_reg(ENCODER_IMAGE_SIZE_REG, (resx << 16) | resy); 137 | encoder_write_reg(ENCODER_START_REG, 7); /* RGB, SOF */ 138 | } 139 | 140 | int encoder_done(void) { 141 | return (encoder_read_reg(ENCODER_STS_REG) & 0x1) == 0; 142 | } 143 | 144 | void encoder_enable(char enable) { 145 | encoder_enabled = enable; 146 | } 147 | 148 | int encoder_set_quality(int quality) { 149 | switch(quality) { 150 | case 100: 151 | case 85: 152 | case 75: 153 | case 50: 154 | encoder_quality = quality; 155 | break; 156 | default: 157 | printf("Unsupported encoder quality (50, 75, 85 or 100)\r\n"); 158 | return 0; 159 | } 160 | return 1; 161 | } 162 | 163 | int encoder_set_fps(int fps) { 164 | if(encoder_target_fps > 0 && encoder_target_fps <= 60) { 165 | encoder_target_fps = fps; 166 | return 0; 167 | } 168 | else { 169 | encoder_target_fps = 30; 170 | return 1; 171 | } 172 | } 173 | 174 | void encoder_service(void) { 175 | 176 | static int last_event; 177 | static int last_fps_event; 178 | static int frame_cnt; 179 | static int can_start; 180 | 181 | if(encoder_enabled) { 182 | if(elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY/encoder_target_fps)) 183 | can_start = 1; 184 | if(can_start & encoder_done()) { 185 | encoder_init(encoder_quality); 186 | encoder_start(processor_h_active, processor_v_active); 187 | can_start = 0; 188 | frame_cnt++; 189 | } 190 | if(encoder_reader_done_read()) { 191 | encoder_reader_h_width_write(processor_h_active); 192 | encoder_reader_v_width_write(processor_v_active); 193 | encoder_reader_start_write(1); 194 | } 195 | if(elapsed(&last_fps_event, SYSTEM_CLOCK_FREQUENCY)) { 196 | encoder_fps = frame_cnt; 197 | frame_cnt = 0; 198 | } 199 | } 200 | } 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /firmware/edid.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "edid.h" 5 | 6 | struct edid { 7 | uint8_t header[8]; 8 | 9 | uint8_t manufacturer[2]; 10 | uint8_t product_code[2]; 11 | uint8_t serial_number[4]; 12 | uint8_t manufacture_week; 13 | uint8_t manufacture_year; 14 | 15 | uint8_t edid_version; 16 | uint8_t edid_revision; 17 | 18 | uint8_t video_input; 19 | uint8_t h_image_size; 20 | uint8_t v_image_size; 21 | uint8_t gamma; 22 | uint8_t feature_support; 23 | 24 | uint8_t cc_rg_l; 25 | uint8_t cc_bw_l; 26 | uint8_t cc_rx_h; 27 | uint8_t cc_ry_h; 28 | uint8_t cc_gx_h; 29 | uint8_t cc_gy_h; 30 | uint8_t cc_bx_h; 31 | uint8_t cc_by_h; 32 | uint8_t cc_wx_h; 33 | uint8_t cc_wy_h; 34 | 35 | uint8_t est_timings_1; 36 | uint8_t est_timings_2; 37 | uint8_t rsv_timings; 38 | 39 | uint8_t timings_std[16]; 40 | 41 | uint8_t data_blocks[4][18]; 42 | 43 | uint8_t ext_block_count; 44 | 45 | uint8_t checksum; 46 | } __attribute__((packed)); 47 | 48 | struct edid_timing { 49 | uint8_t pixel_clock[2]; 50 | 51 | uint8_t h_active_l; 52 | uint8_t h_blanking_l; 53 | uint8_t h_active_blanking_h; 54 | 55 | uint8_t v_active_l; 56 | uint8_t v_blanking_l; 57 | uint8_t v_active_blanking_h; 58 | 59 | uint8_t h_sync_offset_l; 60 | uint8_t h_sync_width_l; 61 | uint8_t v_sync_offset_width_l; 62 | uint8_t hv_sync_offset_width_h; 63 | 64 | uint8_t h_image_size_l; 65 | uint8_t v_image_size_l; 66 | uint8_t hv_image_size_h; 67 | 68 | uint8_t h_border; 69 | uint8_t v_border; 70 | 71 | uint8_t flags; 72 | } __attribute__((packed)); 73 | 74 | struct edid_descriptor { 75 | uint8_t flag0; 76 | uint8_t flag1; 77 | uint8_t flag2; 78 | uint8_t data_type; 79 | uint8_t flag3; 80 | uint8_t data[13]; 81 | } __attribute__((packed)); 82 | 83 | static const char correct_header[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; 84 | 85 | static uint8_t compute_checksum(struct edid *e) 86 | { 87 | uint8_t *p = (uint8_t *)e; 88 | uint8_t sum; 89 | int i; 90 | 91 | sum = 0; 92 | for(i=0;i<127;i++) 93 | sum += p[i]; 94 | return -sum; 95 | } 96 | 97 | int validate_edid(const void *buf) 98 | { 99 | struct edid *e = (struct edid *)buf; 100 | 101 | if(memcmp(e->header, correct_header, 8) != 0) 102 | return 0; 103 | if(compute_checksum(e) != e->checksum) 104 | return 0; 105 | return 1; 106 | } 107 | 108 | void get_monitor_name(const void *buf, char *name) 109 | { 110 | struct edid *e = (struct edid *)buf; 111 | int i; 112 | uint8_t *data_block; 113 | char *c; 114 | 115 | name[0] = 0; 116 | 117 | data_block = NULL; 118 | for(i=0;i<4;i++) 119 | if((e->data_blocks[i][0] == 0x00) 120 | && (e->data_blocks[i][1] == 0x00) 121 | && (e->data_blocks[i][2] == 0x00) 122 | && (e->data_blocks[i][3] == 0xfc)) { 123 | data_block = e->data_blocks[i]; 124 | break; 125 | } 126 | if(!data_block) 127 | return; 128 | 129 | name[MAX_MONITOR_NAME_LEN] = 0; 130 | memcpy(name, &data_block[5], MAX_MONITOR_NAME_LEN); 131 | c = strchr(name, '\n'); 132 | if(c) 133 | *c = 0; 134 | } 135 | 136 | static void generate_edid_timing(uint8_t *data_block, const struct video_timing *timing) 137 | { 138 | struct edid_timing *t = (struct edid_timing *)data_block; 139 | unsigned int h_image_size, v_image_size; 140 | 141 | t->pixel_clock[0] = timing->pixel_clock & 0xff; 142 | t->pixel_clock[1] = timing->pixel_clock >> 8; 143 | 144 | t->h_active_l = timing->h_active & 0xff; 145 | t->h_blanking_l = timing->h_blanking & 0xff; 146 | t->h_active_blanking_h = ((timing->h_active >> 8) << 4) | (timing->h_blanking >> 8); 147 | 148 | t->v_active_l = timing->v_active & 0xff; 149 | t->v_blanking_l = timing->v_blanking & 0xff; 150 | t->v_active_blanking_h = ((timing->v_active >> 8) << 4) | (timing->v_blanking >> 8); 151 | 152 | t->h_sync_offset_l = timing->h_sync_offset & 0xff; 153 | t->h_sync_width_l = timing->h_sync_width & 0xff; 154 | t->v_sync_offset_width_l = timing->v_sync_offset & 0xff; 155 | t->hv_sync_offset_width_h = ((timing->h_sync_offset >> 8) << 6) | ((timing->h_sync_width >> 8) << 4) 156 | | ((timing->v_sync_offset >> 8) << 2) | (timing->v_sync_width >> 8); 157 | 158 | h_image_size = 10*timing->h_active/64; 159 | v_image_size = 10*timing->v_active/64; 160 | t->h_image_size_l = h_image_size & 0xff; 161 | t->v_image_size_l = v_image_size & 0xff; 162 | t->hv_image_size_h = ((h_image_size >> 8) << 4) | (v_image_size >> 8); 163 | 164 | t->h_border = 0; 165 | t->v_border = 0; 166 | 167 | t->flags = 0x1e; 168 | } 169 | 170 | static void generate_monitor_name(uint8_t *data_block, const char *name) 171 | { 172 | struct edid_descriptor *d = (struct edid_descriptor *)data_block; 173 | int i; 174 | 175 | d->flag0 = d->flag1 = d->flag2 = d->flag3 = 0; 176 | d->data_type = 0xfc; 177 | for(i=0;i<12;i++) { 178 | if(!name[i]) 179 | break; 180 | d->data[i] = name[i]; 181 | } 182 | d->data[i++] = 0x0a; 183 | for(;i<13;i++) 184 | d->data[i] = 0x20; 185 | } 186 | 187 | static void generate_unused(uint8_t *data_block) 188 | { 189 | struct edid_descriptor *d = (struct edid_descriptor *)data_block; 190 | 191 | memset(d, 0, sizeof(struct edid_descriptor)); 192 | d->data_type = 0x10; 193 | } 194 | 195 | void generate_edid(void *out, 196 | const char mfg_name[3], const char product_code[2], int year, 197 | const char *name, 198 | const struct video_timing *timing) 199 | { 200 | struct edid *e = (struct edid *)out; 201 | int i, j, k; 202 | 203 | memcpy(e->header, correct_header, 8); 204 | 205 | i = mfg_name[0] - 'A' + 1; 206 | j = mfg_name[1] - 'A' + 1; 207 | k = mfg_name[2] - 'A' + 1; 208 | e->manufacturer[0] = (i << 2) | (j >> 3); 209 | e->manufacturer[1] = ((j & 0x07) << 5) | k; 210 | e->product_code[0] = product_code[0]; e->product_code[1] = product_code[1]; 211 | e->serial_number[0] = e->serial_number[1] = e->serial_number[2] = e->serial_number[3] = 0; 212 | e->manufacture_week = 0; 213 | e->manufacture_year = year - 1990; 214 | 215 | e->edid_version = 1; 216 | e->edid_revision = 3; 217 | 218 | e->video_input = 0x80; /* digital */ 219 | e->h_image_size = timing->h_active/64; 220 | e->v_image_size = timing->v_active/64; 221 | e->gamma = 0x78; 222 | e->feature_support = 0x06; 223 | 224 | e->cc_rg_l = 0; 225 | e->cc_bw_l = 0; 226 | e->cc_rx_h = 0; 227 | e->cc_ry_h = 0; 228 | e->cc_gx_h = 0; 229 | e->cc_gy_h = 0; 230 | e->cc_bx_h = 0; 231 | e->cc_by_h = 0; 232 | e->cc_wx_h = 0; 233 | e->cc_wy_h = 0; 234 | 235 | e->est_timings_1 = timing->established_timing >> 8; 236 | e->est_timings_2 = timing->established_timing & 0xff; 237 | e->rsv_timings = 0; 238 | memset(e->timings_std, 0x01, 16); 239 | 240 | generate_edid_timing(e->data_blocks[0], timing); 241 | generate_monitor_name(e->data_blocks[1], name); 242 | generate_unused(e->data_blocks[2]); 243 | generate_unused(e->data_blocks[3]); 244 | 245 | e->ext_block_count = 0; 246 | 247 | e->checksum = compute_checksum(e); 248 | } 249 | 250 | unsigned calculate_refresh_rate(const struct video_timing* mode) 251 | { 252 | unsigned int refresh_span; 253 | refresh_span = (mode->h_active + mode->h_blanking)*(mode->v_active + mode->v_blanking); 254 | return mode->pixel_clock*10000/refresh_span; 255 | } 256 | -------------------------------------------------------------------------------- /overlay/hdcp_lfsr.v: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2011, Andrew "bunnie" Huang 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, 9 | // this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | // POSSIBILITY OF SUCH DAMAGE. 24 | // 25 | ////////////////////////////////////////////////////////////////////////////// 26 | 27 | `timescale 1 ns / 1 ps 28 | 29 | // lfsr module for hdcp cipher 30 | module hdcp_lfsr( 31 | input wire clk, 32 | input wire reset, // this is just a low-level reset 33 | 34 | input wire [55:0] iv, // initial values 35 | 36 | input wire init_iv, // load initial values 37 | input wire advance, // advance state one cycle 38 | output wire onebit // my one-bit output 39 | ); 40 | 41 | reg [12:0] lfsr0; 42 | reg [13:0] lfsr1; 43 | reg [15:0] lfsr2; 44 | reg [16:0] lfsr3; 45 | 46 | wire [3:0] comb_tap0; 47 | wire [3:0] comb_tap1; 48 | wire [3:0] comb_tap2; 49 | 50 | always @(posedge clk or posedge reset) begin 51 | if( reset == 1 ) begin 52 | lfsr0 <= 13'b0; 53 | lfsr1 <= 14'b0; 54 | lfsr2 <= 16'b0; 55 | lfsr3 <= 17'b0; 56 | end else begin 57 | if( init_iv ) begin 58 | // assume bit-offsets start from 0 59 | lfsr0[12:0] <= {~iv[6],iv[11:0]}; 60 | lfsr1[13:0] <= {~iv[18],iv[24:12]}; 61 | lfsr2[15:0] <= {~iv[32],iv[39:25]}; 62 | lfsr3[16:0] <= {~iv[47],iv[55:40]}; 63 | end else if( advance ) begin 64 | // 13 11 9 5 65 | // 12 10 8 4 66 | lfsr0[0 ] <= lfsr0[4] ^ lfsr0[8] ^ lfsr0[10] ^ lfsr0[12]; 67 | lfsr0[1 ] <= lfsr0[0 ]; 68 | lfsr0[2 ] <= lfsr0[1 ]; 69 | lfsr0[3 ] <= lfsr0[2 ]; 70 | lfsr0[4 ] <= lfsr0[3 ]; 71 | lfsr0[5 ] <= lfsr0[4 ]; 72 | lfsr0[6 ] <= lfsr0[5 ]; 73 | lfsr0[7 ] <= lfsr0[6 ]; 74 | lfsr0[8 ] <= lfsr0[7 ]; 75 | lfsr0[9 ] <= lfsr0[8 ]; 76 | lfsr0[10] <= lfsr0[9 ]; 77 | lfsr0[11] <= lfsr0[10]; 78 | lfsr0[12] <= lfsr0[11]; 79 | 80 | //4 6 7 10 11 14 81 | //3 5 6 9 10 13 82 | lfsr1[0 ] <= lfsr1[3] ^ lfsr1[5] ^ lfsr1[6] ^ lfsr1[9] ^ lfsr1[10] ^ lfsr1[13]; 83 | lfsr1[1 ] <= lfsr1[0 ]; 84 | lfsr1[2 ] <= lfsr1[1 ]; 85 | lfsr1[3 ] <= lfsr1[2 ]; 86 | lfsr1[4 ] <= lfsr1[3 ]; 87 | lfsr1[5 ] <= lfsr1[4 ]; 88 | lfsr1[6 ] <= lfsr1[5 ]; 89 | lfsr1[7 ] <= lfsr1[6 ]; 90 | lfsr1[8 ] <= lfsr1[7 ]; 91 | lfsr1[9 ] <= lfsr1[8 ]; 92 | lfsr1[10] <= lfsr1[9 ]; 93 | lfsr1[11] <= lfsr1[10]; 94 | lfsr1[12] <= lfsr1[11]; 95 | lfsr1[13] <= lfsr1[12]; 96 | 97 | //5 7 8 12 15 16 98 | //4 6 7 11 14 15 99 | lfsr2[0 ] <= lfsr2[4] ^ lfsr2[6] ^ lfsr2[7] ^ lfsr2[11] ^ lfsr2[14] ^ lfsr2[15]; 100 | lfsr2[1 ] <= lfsr2[0 ]; 101 | lfsr2[2 ] <= lfsr2[1 ]; 102 | lfsr2[3 ] <= lfsr2[2 ]; 103 | lfsr2[4 ] <= lfsr2[3 ]; 104 | lfsr2[5 ] <= lfsr2[4 ]; 105 | lfsr2[6 ] <= lfsr2[5 ]; 106 | lfsr2[7 ] <= lfsr2[6 ]; 107 | lfsr2[8 ] <= lfsr2[7 ]; 108 | lfsr2[9 ] <= lfsr2[8 ]; 109 | lfsr2[10] <= lfsr2[9 ]; 110 | lfsr2[11] <= lfsr2[10]; 111 | lfsr2[12] <= lfsr2[11]; 112 | lfsr2[13] <= lfsr2[12]; 113 | lfsr2[14] <= lfsr2[13]; 114 | lfsr2[15] <= lfsr2[14]; 115 | 116 | //5 11 15 17 117 | //4 10 14 16 118 | lfsr3[0 ] <= lfsr3[4] ^ lfsr3[10] ^ lfsr3[14] ^ lfsr3[16]; 119 | lfsr3[1 ] <= lfsr3[0 ]; 120 | lfsr3[2 ] <= lfsr3[1 ]; 121 | lfsr3[3 ] <= lfsr3[2 ]; 122 | lfsr3[4 ] <= lfsr3[3 ]; 123 | lfsr3[5 ] <= lfsr3[4 ]; 124 | lfsr3[6 ] <= lfsr3[5 ]; 125 | lfsr3[7 ] <= lfsr3[6 ]; 126 | lfsr3[8 ] <= lfsr3[7 ]; 127 | lfsr3[9 ] <= lfsr3[8 ]; 128 | lfsr3[10] <= lfsr3[9 ]; 129 | lfsr3[11] <= lfsr3[10]; 130 | lfsr3[12] <= lfsr3[11]; 131 | lfsr3[13] <= lfsr3[12]; 132 | lfsr3[14] <= lfsr3[13]; 133 | lfsr3[15] <= lfsr3[14]; 134 | lfsr3[16] <= lfsr3[15]; 135 | end else begin 136 | // hold state 137 | lfsr0 <= lfsr0; 138 | lfsr1 <= lfsr1; 139 | lfsr2 <= lfsr2; 140 | lfsr3 <= lfsr3; 141 | end 142 | end // else: !if( reset == 1 ) 143 | end // always @ (posedge clk or posedge reset) 144 | 145 | assign comb_tap0[0] = lfsr0[3]; 146 | assign comb_tap0[1] = lfsr1[4]; 147 | assign comb_tap0[2] = lfsr2[5]; 148 | assign comb_tap0[3] = lfsr3[5]; 149 | 150 | assign comb_tap1[0] = lfsr0[7]; 151 | assign comb_tap1[1] = lfsr1[8]; 152 | assign comb_tap1[2] = lfsr2[9]; 153 | assign comb_tap1[3] = lfsr3[11]; 154 | 155 | assign comb_tap2[0] = lfsr0[12]; 156 | assign comb_tap2[1] = lfsr1[13]; 157 | assign comb_tap2[2] = lfsr2[15]; 158 | assign comb_tap2[3] = lfsr3[16]; 159 | 160 | wire [3:0] sh_dout; 161 | shuffle_network sh0 ( .clk(clk), .reset(reset), 162 | .din(comb_tap0[0] ^ comb_tap0[1] ^ comb_tap0[2] ^ comb_tap0[3]), 163 | .sel(comb_tap1[0]), 164 | .advance(advance), 165 | .init_iv(init_iv), 166 | .dout(sh_dout[0]) 167 | ); 168 | 169 | shuffle_network sh1 ( .clk(clk), .reset(reset), 170 | .din(sh_dout[0]), 171 | .sel(comb_tap1[1]), 172 | .advance(advance), 173 | .init_iv(init_iv), 174 | .dout(sh_dout[1]) 175 | ); 176 | 177 | shuffle_network sh2 ( .clk(clk), .reset(reset), 178 | .din(sh_dout[1]), 179 | .sel(comb_tap1[2]), 180 | .advance(advance), 181 | .init_iv(init_iv), 182 | .dout(sh_dout[2]) 183 | ); 184 | 185 | shuffle_network sh3 ( .clk(clk), .reset(reset), 186 | .din(sh_dout[2]), 187 | .sel(comb_tap1[3]), 188 | .advance(advance), 189 | .init_iv(init_iv), 190 | .dout(sh_dout[3]) 191 | ); 192 | 193 | assign onebit = sh_dout[3] ^ comb_tap2[0] ^ comb_tap2[1] ^ comb_tap2[2] ^ comb_tap2[3]; 194 | 195 | endmodule // hdcp_lfsr 196 | 197 | -------------------------------------------------------------------------------- /software/pcie/user/litepcie_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * LitePCIe utilities 3 | * 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "litepcie.h" 16 | #include "cutils.h" 17 | #include "config.h" 18 | #include "csr.h" 19 | #include "flags.h" 20 | #include "litepcie_lib.h" 21 | 22 | static inline uint32_t seed_to_data(uint32_t seed) 23 | { 24 | #if 1 25 | /* more random but slower */ 26 | return seed * 0x31415976 + 1; 27 | #else 28 | /* simplify debug: just copy the counter */ 29 | return seed; 30 | #endif 31 | } 32 | 33 | static void write_pn_data(uint32_t *dst, int count, uint32_t *pseed) 34 | { 35 | int i; 36 | uint32_t seed; 37 | 38 | seed = *pseed; 39 | for(i = 0; i < count; i++) { 40 | dst[i] = seed_to_data(seed); 41 | seed++; 42 | } 43 | *pseed = seed; 44 | } 45 | 46 | /* Return the number of errors */ 47 | static int check_pn_data(const uint32_t *tab, int count, 48 | uint32_t *pseed) 49 | { 50 | int i, errors; 51 | uint32_t seed; 52 | 53 | errors = 0; 54 | seed = *pseed; 55 | for(i = 0; i < count; i++) { 56 | if (tab[i] != seed_to_data(seed)) { 57 | errors++; 58 | } 59 | seed++; 60 | } 61 | *pseed = seed; 62 | return errors; 63 | } 64 | 65 | #define MAX_SHIFT_OFFSET 128 66 | 67 | /* test DMA with a buffer size of buf_size bytes in loopback 68 | mode. */ 69 | void dma_test(LitePCIeState *s, int buf_size, int buf_count, BOOL is_loopback) 70 | { 71 | int is_first, tx_buf_num, buf_num_cur, buf_num_next; 72 | struct litepcie_ioctl_dma_wait dma_wait; 73 | int buf_stats_count; /* statistics */ 74 | int64_t last_time; 75 | uint32_t tx_seed, rx_seed; 76 | int buf_rx_count, first_rx_buf, rx_errors, shift, d, tx_underflows; 77 | 78 | litepcie_dma_start(s, buf_size, buf_count, is_loopback); 79 | 80 | is_first = 1; 81 | buf_num_cur = 0; /* next buffer to receive */ 82 | /* PN data TX and RX state */ 83 | tx_seed = MAX_SHIFT_OFFSET; 84 | rx_seed = 0; 85 | buf_rx_count = 0; 86 | first_rx_buf = 1; 87 | 88 | /* statistics */ 89 | buf_stats_count = 0; 90 | last_time = litepcie_get_time_ms(); 91 | rx_errors = 0; 92 | shift = 0; 93 | tx_underflows = 0; 94 | 95 | for(;;) { 96 | /* wait until at least one buffer is received */ 97 | dma_wait.timeout = 1000; /* 1 second timeout */ 98 | dma_wait.tx_wait = FALSE; 99 | dma_wait.tx_buf_num = -1; /* not used */ 100 | if (is_first) { 101 | dma_wait.rx_buf_num = -1; /* don't wait, just get the last 102 | received buffer number */ 103 | } else { 104 | dma_wait.rx_buf_num = sub_mod_int(buf_num_cur, 1, buf_count); 105 | } 106 | /* wait until the current buffer number is different from 107 | dma_wait.buf_num */ 108 | if (ioctl(s->litepcie_fd, LITEPCIE_IOCTL_DMA_WAIT, &dma_wait) < 0) { 109 | perror("LITEPCIE_IOCTL_DMA_WAIT"); 110 | } 111 | if (is_first) { 112 | buf_num_cur = dma_wait.rx_buf_num; 113 | is_first = 0; 114 | } 115 | buf_num_next = add_mod_int(dma_wait.rx_buf_num, 1, buf_count); 116 | 117 | while (buf_num_cur != buf_num_next) { 118 | 119 | /* write the TX data 4/10 of a DMA cycle in the future */ 120 | tx_buf_num = add_mod_int(buf_num_cur, 4*buf_count/10, buf_count); 121 | d = sub_mod_int(tx_buf_num, buf_num_next, buf_count); 122 | if (d >= (buf_count / 2)) { 123 | /* we are too late in writing data, which necessarily 124 | gives read errors. */ 125 | tx_underflows++; 126 | } 127 | 128 | write_pn_data((uint32_t *)(s->dma_tx_buf + 129 | tx_buf_num * s->dma_tx_buf_size), 130 | s->tx_buf_size >> 2, &tx_seed); 131 | 132 | if (buf_rx_count >= 4*buf_count/10) { 133 | const uint32_t *rx_buf; 134 | int rx_buf_len; 135 | 136 | rx_buf = (uint32_t *)(s->dma_rx_buf + buf_num_cur * s->dma_rx_buf_size); 137 | rx_buf_len = s->rx_buf_size >> 2; 138 | 139 | if (first_rx_buf) { 140 | uint32_t seed; 141 | 142 | /* find the initial shift */ 143 | for(shift = 0; shift < 2 * MAX_SHIFT_OFFSET; shift++) { 144 | seed = rx_seed + shift; 145 | rx_errors = check_pn_data(rx_buf, rx_buf_len, &seed); 146 | if (rx_errors <= (rx_buf_len / 2)) { 147 | rx_seed = seed; 148 | break; 149 | } 150 | } 151 | if (shift == 2 * MAX_SHIFT_OFFSET) { 152 | printf("Cannot find initial data\n"); 153 | exit(1); 154 | } else { 155 | printf("RX shift = %d\n", 156 | -(shift - MAX_SHIFT_OFFSET)); 157 | } 158 | first_rx_buf = 0; 159 | } else { 160 | /* count the number of errors */ 161 | rx_errors += check_pn_data(rx_buf, rx_buf_len, &rx_seed); 162 | } 163 | } else { 164 | buf_rx_count++; 165 | } 166 | 167 | buf_num_cur = add_mod_int(buf_num_cur, 1, buf_count); 168 | 169 | /* statistics */ 170 | if (++buf_stats_count == 10000) { 171 | int64_t duration; 172 | duration = litepcie_get_time_ms() - last_time; 173 | printf("%0.1f Gb/sec %0.1f bufs/sec tx_underflows=%d errors=%d\n", 174 | (double)buf_stats_count * buf_size * 8 / ((double)duration * 1e6), 175 | (double)buf_stats_count * 1000 / (double)duration, 176 | tx_underflows, rx_errors); 177 | last_time = litepcie_get_time_ms(); 178 | buf_stats_count = 0; 179 | tx_underflows = 0; 180 | rx_errors = 0; 181 | } 182 | } 183 | } 184 | 185 | litepcie_dma_stop(s); 186 | } 187 | 188 | void dma_loopback_test(void) 189 | { 190 | LitePCIeState *s; 191 | 192 | s = litepcie_open(LITEPCIE_FILENAME); 193 | if (!s) { 194 | fprintf(stderr, "Could not init driver\n"); 195 | exit(1); 196 | } 197 | dma_test(s, 16*1024, DMA_BUFFER_COUNT, TRUE); 198 | 199 | litepcie_close(s); 200 | } 201 | 202 | void dump_version(void) 203 | { 204 | LitePCIeState *s; 205 | int i; 206 | unsigned char fpga_identification[256]; 207 | 208 | s = litepcie_open(LITEPCIE_FILENAME); 209 | if (!s) { 210 | fprintf(stderr, "Could not init driver\n"); 211 | exit(1); 212 | } 213 | 214 | for(i=0; i<256; i++) 215 | fpga_identification[i] = litepcie_readl(s, CSR_IDENTIFIER_MEM_BASE + 4*i); 216 | printf("FPGA identification=%s\n", fpga_identification); 217 | 218 | litepcie_close(s); 219 | } 220 | 221 | void help(void) 222 | { 223 | printf("usage: litepcie_util cmd [args...]\n" 224 | "\n" 225 | "available commands:\n" 226 | "dma_loopback_test test DMA loopback operation\n" 227 | "version return fpga version\n" 228 | ); 229 | exit(1); 230 | } 231 | 232 | int main(int argc, char **argv) 233 | { 234 | const char *cmd; 235 | int c; 236 | 237 | for(;;) { 238 | c = getopt(argc, argv, "h"); 239 | if (c == -1) 240 | break; 241 | switch(c) { 242 | case 'h': 243 | help(); 244 | break; 245 | default: 246 | exit(1); 247 | } 248 | } 249 | 250 | if (optind >= argc) 251 | help(); 252 | cmd = argv[optind++]; 253 | 254 | if (!strcmp(cmd, "dma_loopback_test")) { 255 | dma_loopback_test(); 256 | } else if (!strcmp(cmd, "version")) { 257 | dump_version(); 258 | } else { 259 | help(); 260 | } 261 | 262 | return 0; 263 | } 264 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | # NeTV2 LiteX Quickstart Guide 2 | 3 | Please check out the wiki (https://github.com/AlphamaxMedia/netv2-fpga/wiki) for more documentation on the NeTV2 project. 4 | 5 | ## Updates 6 | Grab the latest update by first ensuring the NeTV2 is on the network, then plugging a keyboard and mouse into the NeTV2's USB ports and typing 'ctrl-q'. Double click the "Update FPGA" icon that appears on the left hand side of the screen. The screen should blank for about a minute, and then come back. 7 | 8 | As of September 2019, the latest update enables 1080p, 1080i, and 720p auto-resolution support, and you can also pick YCrCb chroma coding for the overlay. If your overlay shows up looking pink and green, this is likely the issue. You can pick YCrCb by logging into your NeTV2 via SSH and running the command ~/code/netv2mvp-scripts/set_ycrcb.sh. This command only appears after the latest update. 9 | 10 | ## Hacking 11 | Note that the NeTV2 hardware uses primarily torx screws. You will need a Torx T6 bit to open the case, and a 2.5mm hex key (or Torx T10) bit to remove the motherboard from its standoffs. 12 | 13 | If you're using the NeTV2 as "just a board" without the Raspberry Pi attached, please read https://github.com/AlphamaxMedia/netv2-fpga/wiki/Using-NeTV2-as-a-Dev-Board for more on how to use it. 14 | 15 | *IMPORTANT*: If you want to connect a regular HDMI cable to the "overlay" port, you will need to re-build the FPGA with the correct pattern of differential pair inversions to match an HDMI cable. See https://github.com/AlphamaxMedia/netv2-fpga/blob/master/netv2mvp.py#L148. The pattern of inversions checked into the master branch targets the custom M2M jumper, which swaps some differential pairs to improve routing. 16 | 17 | These instructions can be used to quickly start FPGA development. 18 | 19 | ## tl;dr: ## 20 | 21 | 1. Check out this repo with `git clone --recurse-submodules https://github.com/AlphamaxMedia/netv2-fpga`. 22 | 1. Ensure you have Python 3.5 or newer installed. 23 | 1. Ensure you have `make` installed. 24 | 1. Download the Risc-V toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases and put it in your PATH. 25 | 1. Go to https://www.xilinx.com/support/download.html and download `All OS installer Single-File Download` 26 | 1. Do a minimal Xilinx install to /opt/Xilinx/, and untick everything except `Design Tools / Vivado Design Suite / Vivado` and `Devices / Production Devices / 7 Series` 27 | 1. Go to https://www.xilinx.com/member/forms/license-form.html, get a license, and place it in ~/.Xilinx/Xilinx.lic 28 | 1. Run `./netv2mvp.py` (or `python3 ./netv2mvp.py`) 29 | 30 | ### Source code update 31 | 32 | To update the repo to the upstream version, including all dependencies, run: 33 | 34 | ```sh 35 | git pull 36 | git submodule update --recursive 37 | ``` 38 | 39 | ### Using lxbuildenv.py Environment 40 | 41 | `lxbuildenv` is a Python module. It sets up the build environment and ensures you have the correct dependencies. To use it, start your program off with: 42 | 43 | ```python 44 | #!/usr/bin/env python3 45 | import lxbuildenv 46 | ``` 47 | 48 | *`lxbuildenv.py` has some very surprising behaviors* that you should be aware of: 49 | 50 | 1. In order to set environment variables such as `PYTHONHASHSEED`, `lxbuildenv` will actually re-exec the Python interpreter. This will, among other things, cause the pid to change. *This is why lxbuildenv should be imported first*. 51 | 1. The environment variable `PYTHONPATH` is replaced to include every directory under `deps/`. If you rely on `PYTHONPATH` to be something else, this may surprise you. 52 | 1. `lxbuildenv` has several command line parameters that it can accept. To display these, run your command with the `--lx-help` parameter. 53 | 1. The `deps/` directory includes its own `site.py` implementation, adapted from a Debian implementation. This is because some distros force `/usr/share/python/site-packages/` to be first in the dependency list, which causes confusing dependency interactions. If you're relying on site packages being in a certain order, this may cause problems. You can try deleting `deps/site/` in order to disable this behavior. 54 | 55 | In exchange for some deviation from other build environments, `lxbuildenv` gives you several benefits that come in handy for hardware projects: 56 | 57 | 1. Python dicts enumerate in constant order, giving some consistency to build results. 58 | 1. You will probably be modifying code in the dependencies. By keeping them inside the project directory, this becomes much simpler. 59 | 1. Additionally, all dependencies remain under version control, which you would otherwise lose when installing dependencies as packages. 60 | 1. Hardware, moreso than software, depends on exact version numbers. By using `git` to track dependency versions, this build becomes more reproducible. 61 | 1. It is cross-platform, and works anywhere Xilinx does. 62 | 1. The `lxbuildenv` environment doesn't rely on opaque environment variables, or otherwise have a special environment you enter. Everything is documented behind `--help` flags. 63 | 64 | ### Working with Dependencies 65 | 66 | Dependencies are managed through `git`, and managing their usage is largely an exercise 67 | in working with `git`. 68 | 69 | For example, if you would like to make a change to `litex`, go into `deps/litex` and checkout 70 | a new branch and create a new upstream repo. If you're working on Github, you would do 71 | something like fork the repo to your own organization. 72 | 73 | As an example, assume `sutajiokousagi` has forked upstream `litex`: 74 | 75 | ```sh 76 | $ cd deps/litex 77 | $ git checkout -b new-feature 78 | $ git remote add kosagi git@github.com:sutajiokousagi/litex.git 79 | $ cd - 80 | ``` 81 | 82 | Then, make changes to `deps/litex` as needed. 83 | 84 | When you want to merge changes upstream, go into `deps/litex/` and push the branch to your remote: 85 | 86 | ```sh 87 | $ cd deps/litex 88 | $ git push kosagi new-feature 89 | $ cd - 90 | ``` 91 | 92 | Then you can go and open a Pull Request on Github. 93 | 94 | ### Fetching Source Code Updates 95 | 96 | Dependencies are designed to be independent, and you should update them as needed. To update a particular 97 | dependency, go into that dependency's subdirectory and run `git pull`. You may also find it easier to 98 | pull updates from a particular dependency and merge them. For example, if you're working on the `new-feature` 99 | branch of `litex` and want to pull changes from upstream, run: 100 | 101 | ```sh 102 | $ cd deps/litex 103 | $ git fetch origin 104 | $ git merge master 105 | $ cd - 106 | ``` 107 | 108 | This will merge all changes from upstream onto your own branch. 109 | 110 | ### Rationale 111 | 112 | NeTV2 uses Migen for its HDL, and uses many components from the LiteX project. 113 | These are primarily written in Python, which has a large number of options 114 | available for configuring installs, ranging from global installs, virutlalenv, conda, 115 | as well as several others. Everyone has an opinion on what's right. 116 | 117 | These instructions ignore all of that, in favor of simplicity. You likely already 118 | have a copy of `make` and `python`, and you probably have a compiler 119 | installed for other projects. It's also more challenging to work on submodules 120 | when they're combined together in a `site-packages` repository and outside of version control. 121 | 122 | `lxbuildenv` takes a different approach in that it doubles-down on using native 123 | components and simply modifies several magical environment variables to make 124 | it all work. As a bonus, it works on platforms where Conda doesn't, such as 125 | platforms where packages might not be available. 126 | 127 | ## Support programs ## 128 | 129 | There is a wrapper script in this repo to run support programs such as `litex_server` and `litex_term`. These may be invoked either with python (`python bin/litex_server udp`) or on shebang-aware systems they may be executed directly (`./bin/litex_server udp`). 130 | 131 | ## Xilinx PATH ## 132 | 133 | If your Xilinx install is in the default path (`C:\\Xilinx` on Windows, `/opt/Xilinx` on Linux), then the build system should be able to automatically find Xilinx. 134 | 135 | If not, you can add the Xilinx `bin` directory to your PATH. 136 | 137 | ## PyCharm integration ## 138 | 139 | To use PyCharm, open this directory as a `Project` by going to the *File* menu and selecting *Open...*. Make sure you open the entire directory, and not just a single file in this directory. 140 | 141 | When you first open this project, you'll see lots of red squiggly lines indicating errors. PyCharm needs to know about the dependency structure in order to allow you to drill down into modules and auto-complete statements. 142 | 143 | Open this directory in PyCharm and expand the `deps/` directory. Then hold down `Shift` and select all subdirectories under `deps/`. This will include `litedram`, `liteeth`, and so on. 144 | 145 | Then, right-click and select `Mark directory as...` and select `Sources Root`. The red squiggly lines should go away, and PyCharm should now be configured. 146 | 147 | When running your module from within PyCharm, you may find it useful to set environment variables. You can use the `--lx-print-env` command. For example: `./netv2mvp.py --lx-print-env > pycharm.env` to create a `.env`-compatible file. There are several PyCharm plugins that can make use of this file. 148 | 149 | ## Visual Studio Code integration ## 150 | 151 | Visual Studio Code needs to know where modules are. These are specified in environment variables, which are automatically read from a .env file in your project root. Create this file to enable `pylint` and debugging in Visual Studio Code: 152 | 153 | ```sh 154 | $ python ./netv2mvp.py --lx-print-env > .env 155 | ``` 156 | 157 | # Production scripts 158 | There's a number of scripts used to assist with integration into the Raspberry Pi runtime environment. They are all located in https://github.com/alphamaxmedia/netv2mvp-scripts/. In this location, you'll find the scripts that do the one-click update, openocd manipulation of bitstream and SPI ROM, publishing of status info to JSON feed, and Magic Mirror config info. This repository is just for the FPGA design. 159 | -------------------------------------------------------------------------------- /overlay/phsaligner.v: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Xilinx, Inc. 2010 www.xilinx.com 4 | // 5 | // XAPP xxx - TMDS serial stream phase aligner 6 | // 7 | ////////////////////////////////////////////////////////////////////////////// 8 | // 9 | // File name : phasealigner.v 10 | // 11 | // Description : This module determines whether the Spartan-6 IOSERDES 12 | // has validate the incoming TMDS data stream 13 | // 14 | // 15 | // Note: 16 | // 17 | // Author : Bob Feng 18 | // 19 | // Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are 20 | // provided to you "as is". Xilinx and its licensors make and you 21 | // receive no warranties or conditions, express, implied, 22 | // statutory or otherwise, and Xilinx specifically disclaims any 23 | // implied warranties of merchantability, non-infringement,or 24 | // fitness for a particular purpose. Xilinx does not warrant that 25 | // the functions contained in these designs will meet your 26 | // requirements, or that the operation of these designs will be 27 | // uninterrupted or error free, or that defects in the Designs 28 | // will be corrected. Furthermore, Xilinx does not warrantor 29 | // make any representations regarding use or the results of the 30 | // use of the designs in terms of correctness, accuracy, 31 | // reliability, or otherwise. 32 | // 33 | // LIMITATION OF LIABILITY. In no event will Xilinx or its 34 | // licensors be liable for any loss of data, lost profits,cost 35 | // or procurement of substitute goods or services, or for any 36 | // special, incidental, consequential, or indirect damages 37 | // arising from the use or operation of the designs or 38 | // accompanying documentation, however caused and on any theory 39 | // of liability. This limitation will apply even if Xilinx 40 | // has been advised of the possibility of such damage. This 41 | // limitation shall apply not-withstanding the failure of the 42 | // essential purpose of any limited remedies herein. 43 | // 44 | // Copyright � 2006 Xilinx, Inc. 45 | // All rights reserved 46 | // 47 | ////////////////////////////////////////////////////////////////////////////// 48 | // 49 | `timescale 1 ns / 1ps 50 | `define SIMULATION 1 51 | 52 | // here's what this module does. 53 | // 1. searches for an incoming control token 54 | // 2. when it finds one, makes sure it sticks around at least CTKNCNTWD. 55 | // 3. if it can't find one within SRCHTIMERWD, try a new bitslip and/or gearbox setting. 56 | // keep on trying bitslips and gear settings over and over again ewvery SRCHTIMERWD 57 | // until we've found a CTKNCNTWD. 58 | // 4. once the token has stuck around for the pre-determined number of cycles, repeat 59 | // the search to confirm it. (this function is degenerate in this implementation) 60 | // 5. once setting has been confirmed, assume it never changes. 61 | 62 | module phsaligner # ( 63 | parameter OPENEYE_CNT_WD = 3, // valid open eye counter width 64 | // parameter CTKNCNTWD = 7, // Control Token Counter Width (dvi spec is 128) 65 | parameter CTKNCNTWD = 3, // Control Token Counter Width (hdmi spec is 8) 66 | parameter SRCHTIMERWD = 24 // Idle Timer Width 67 | ) 68 | ( 69 | input wire rst, 70 | input wire clk, 71 | input wire [9:0] sdata, // 10 bit serial stream sync. to clk 72 | output reg flipgear, 73 | output reg bitslip, 74 | output reg psaligned, // FSM output 75 | input wire search_again 76 | ); 77 | 78 | parameter CTRLTOKEN0 = 10'b1101010100; 79 | parameter CTRLTOKEN1 = 10'b0010101011; 80 | parameter CTRLTOKEN2 = 10'b0101010100; 81 | parameter CTRLTOKEN3 = 10'b1010101011; 82 | 83 | /////////////////////////////////////////////////////// 84 | // Control Token Detection 85 | /////////////////////////////////////////////////////// 86 | reg rcvd_ctkn, rcvd_ctkn_q; 87 | reg blnkbgn; //blank period begins 88 | 89 | always @ (posedge clk) begin 90 | rcvd_ctkn <=#1 ((sdata == CTRLTOKEN0) || (sdata == CTRLTOKEN1) || 91 | (sdata == CTRLTOKEN2) || (sdata == CTRLTOKEN3)); 92 | 93 | rcvd_ctkn_q <=#1 rcvd_ctkn; 94 | blnkbgn <=#1 !rcvd_ctkn_q & rcvd_ctkn; 95 | end 96 | 97 | ///////////////////////////////////////////////////// 98 | // Control Token Search Timer 99 | // 100 | // DVI 1.0 Spec. says periodic blanking should start 101 | // no less than every 50ms or 20HZ 102 | // 2^24 of 74.25MHZ cycles is about 200ms 103 | ///////////////////////////////////////////////////// 104 | reg [(SRCHTIMERWD-1):0] ctkn_srh_timer; 105 | reg ctkn_srh_rst; //FSM output 106 | 107 | always @ (posedge clk) begin 108 | if (ctkn_srh_rst) 109 | ctkn_srh_timer <=#1 {SRCHTIMERWD{1'b0}}; 110 | else 111 | ctkn_srh_timer <=#1 ctkn_srh_timer + 1'b1; 112 | end 113 | 114 | reg ctkn_srh_tout; 115 | always @ (posedge clk) begin 116 | ctkn_srh_tout <=#1 (ctkn_srh_timer == {SRCHTIMERWD{1'b1}}); 117 | end 118 | 119 | ///////////////////////////////////////////////////// 120 | // Contorl Token Event Counter 121 | // 122 | // DVI 1.0 Spec. says the minimal blanking period 123 | // is at least 128 pixels long in order to achieve 124 | // synchronization 125 | // 126 | // HDMI reduces this to as little as 8 127 | ///////////////////////////////////////////////////// 128 | reg [(CTKNCNTWD-1):0] ctkn_counter; 129 | reg ctkn_cnt_rst; //FSM output 130 | 131 | always @ (posedge clk) begin 132 | if(ctkn_cnt_rst) 133 | ctkn_counter <=#1 {CTKNCNTWD{1'b0}}; 134 | else 135 | ctkn_counter <=#1 ctkn_counter + 1'b1; 136 | end 137 | 138 | reg ctkn_cnt_tout; 139 | always @ (posedge clk) begin 140 | ctkn_cnt_tout <=#1 (ctkn_counter == {CTKNCNTWD{1'b1}}); 141 | end 142 | 143 | ////////////////////////////////////////////////////////// 144 | // Below starts the phase alignment state machine 145 | ////////////////////////////////////////////////////////// 146 | parameter INIT = 6'b1 << 0; 147 | parameter SEARCH = 6'b1 << 1; // Searching for control tokens 148 | parameter BITSLIP = 6'b1 << 2; 149 | parameter RCVDCTKN = 6'b1 << 3; // Received at one Control Token and check for more 150 | parameter BLNKPRD = 6'b1 << 4; 151 | parameter PSALGND = 6'b1 << 5; // Phase alignment achieved 152 | parameter nSTATES = 6; 153 | 154 | reg [(nSTATES-1):0] cstate = {{(nSTATES-1){1'b0}}, 1'b1}; //current and next states 155 | reg [(nSTATES-1):0] nstate; 156 | 157 | `ifdef SIMULATION 158 | // synthesis translate_off 159 | reg [8*20:1] state_ascii = "INIT "; 160 | always @(cstate) begin 161 | if (cstate == INIT ) state_ascii <= "INIT "; 162 | else if (cstate == SEARCH ) state_ascii <= "SEARCH "; 163 | else if (cstate == BITSLIP ) state_ascii <= "BITSLIP "; 164 | else if (cstate == RCVDCTKN ) state_ascii <= "RCVDCTKN "; 165 | else if (cstate == BLNKPRD ) state_ascii <= "BLNKPRD "; 166 | else state_ascii <= "PSALGND "; 167 | end 168 | // synthesis translate_on 169 | `endif 170 | 171 | always @ (posedge clk or posedge rst) begin 172 | if (rst || search_again) 173 | cstate <= INIT; 174 | else 175 | cstate <=#1 nstate; 176 | end 177 | 178 | ////////////////////////////////////////////////////////// 179 | // Counter counts number of blank period detected 180 | // in order to qualify the bitslip position 181 | ////////////////////////////////////////////////////////// 182 | parameter BLNKPRD_CNT_WD = 1; 183 | 184 | reg [(BLNKPRD_CNT_WD-1):0] blnkprd_cnt = {BLNKPRD_CNT_WD{1'b0}}; 185 | 186 | always @ (*) begin 187 | case (cstate) //synthesis parallel_case full_case 188 | INIT: begin 189 | nstate = (ctkn_srh_tout) ? SEARCH : INIT; 190 | end 191 | 192 | SEARCH: begin 193 | if(blnkbgn) 194 | nstate = RCVDCTKN; 195 | else 196 | nstate = (ctkn_srh_tout) ? BITSLIP : SEARCH; 197 | end 198 | 199 | BITSLIP: begin 200 | nstate = SEARCH; 201 | end 202 | 203 | RCVDCTKN: begin 204 | if(rcvd_ctkn) 205 | nstate = (ctkn_cnt_tout) ? BLNKPRD : RCVDCTKN; 206 | else 207 | nstate = SEARCH; 208 | end 209 | 210 | BLNKPRD: begin 211 | nstate = (blnkprd_cnt == {BLNKPRD_CNT_WD{1'b1}}) ? PSALGND : SEARCH; 212 | end 213 | 214 | PSALGND: begin 215 | nstate = PSALGND; // Phase aligned so hang around here 216 | end 217 | endcase 218 | end 219 | 220 | reg [2:0] bitslip_cnt; 221 | 222 | always @ (posedge clk or posedge rst) begin 223 | if(rst || search_again) begin 224 | psaligned <=#1 1'b0; //phase alignment success flag 225 | bitslip <=#1 1'b0; 226 | ctkn_srh_rst <=#1 1'b1; //control token search timer reset 227 | ctkn_cnt_rst <=#1 1'b1; //control token counter reset 228 | 229 | bitslip <=#1 1'b0; 230 | bitslip_cnt <=#1 3'h0; 231 | flipgear <=#1 1'b0; 232 | blnkprd_cnt <=#1 {BLNKPRD_CNT_WD{1'b0}}; 233 | end else begin 234 | case (cstate) // synthesis parallel_case full_case 235 | INIT: begin 236 | ctkn_srh_rst <=#1 1'b0; 237 | ctkn_cnt_rst <=#1 1'b1; 238 | bitslip <=#1 1'b0; 239 | psaligned <=#1 1'b0; 240 | 241 | bitslip <=#1 1'b0; 242 | bitslip_cnt <=#1 3'h0; 243 | flipgear <=#1 1'b0; 244 | blnkprd_cnt <=#1 {BLNKPRD_CNT_WD{1'b0}}; 245 | end 246 | 247 | SEARCH: begin 248 | ctkn_srh_rst <=#1 1'b0; 249 | ctkn_cnt_rst <=#1 1'b1; 250 | bitslip <=#1 1'b0; 251 | psaligned <=#1 1'b0; 252 | end 253 | 254 | BITSLIP: begin 255 | ctkn_srh_rst <=#1 1'b1; 256 | 257 | bitslip <=#1 1'b1; 258 | bitslip_cnt <=#1 bitslip_cnt + 1'b1; 259 | flipgear <=#1 bitslip_cnt[2]; //bitslip has toggled for 4 times 260 | end 261 | 262 | RCVDCTKN: begin 263 | ctkn_srh_rst <=#1 1'b0; 264 | ctkn_cnt_rst <=#1 1'b0; 265 | end 266 | 267 | BLNKPRD: begin 268 | blnkprd_cnt <=#1 blnkprd_cnt + 1'b1; 269 | end 270 | 271 | PSALGND: begin 272 | psaligned <=#1 1'b1; 273 | end 274 | endcase 275 | end 276 | end 277 | 278 | endmodule 279 | -------------------------------------------------------------------------------- /overlay/hdcp_mod.v: -------------------------------------------------------------------------------- 1 | /* 2 | Integration notes. 3 | 4 | 0x74 DDC traffic format (from i2c_snoop.v): 5 | LSB Bksv MSB 6 | 00: ae 80 12 db fa 00 00 00 00 00 00 00 00 00 00 00 7 | 10: ed 90 b4 5c 0f 00 00 00 46 84 e5 6c 78 b6 53 f5 8 | lSB Aksv MSB LSB An MSB 9 | 10 | 1. Km is computed by the host, based on observations of Aksv/Bksv. 11 | Note that Km is different for every source/sink combo but stays 12 | constant for a given pairing. Note endianness of records. 13 | 14 | 2. An (session key) is recovered on a per-session basis. 15 | 16 | 3. When the 14th byte of i2c_snoop (MSB of Aksv) is written, cipher 17 | initialization is triggered. This is the Aksv14_write signal. 18 | Consider refactor to pass on as an interrupt to SoC to compute 19 | Km dynamically, to avoid HPD re-flash/re-sync. Computations 20 | must complete within 100ms. 21 | 22 | Things to check on integration: 23 | - line_end pulses on the last pixel (not after the last pixel) 24 | This means the line_end pulse can't be a simple de & ~de computation. 25 | Options include anticipating de length based on video mode, or digging 26 | the de signal out from an earlier pipeline stage. 27 | 28 | - hdcp_ena will come from the TERC4/control signal state machine 29 | and needs to be asserted whenever TERC4 islands occur, or when video 30 | data is present (this comes from looking at the guardbands & CTL signals) 31 | 32 | - ctl_code is the 4-bit control code that rides on the r/g channels (b has 33 | hsync/vsync). Necessary for EESS detection. 34 | 35 | - de, hsync, vsync are the video timing signals 36 | 37 | - Km_valid is status feedback from the CPU to indicate when the Km has 38 | been computed by the host and is in fact valid. 39 | 40 | */ 41 | 42 | module hdcp_mod ( 43 | input wire clk, // pixclk 44 | input wire rst, 45 | input wire de, 46 | input wire hsync, // positive active 47 | input wire vsync, 48 | input wire line_end, // need to double check the purpose but nominally de && (sdata == CTRLTOKEN*) 49 | input wire hpd, // high == no cable present 50 | input wire Aksv14_write, // strobe to indicate ksv was written 51 | input wire [63:0] An, 52 | input wire [55:0] Km, 53 | input wire Km_valid, 54 | input wire hdcp_ena, 55 | input wire [3:0] ctl_code, // control code 56 | output wire [23:0] cipher_stream, 57 | // output wire [17:0] hdcp_debug, 58 | // output wire [12:0] cipher_debug, 59 | // output wire [7:0] An_debug, 60 | // output wire [7:0] Km_debug, 61 | output wire stream_ready 62 | ); 63 | 64 | reg Km_rdy0; 65 | reg Km_rdy1; 66 | reg Km_rdy2; 67 | wire Km_ready; 68 | reg hdcp_requested; 69 | 70 | // confirm the correct byte ordering 71 | // assign An_debug = An[63:56]; 72 | // assign Km_debug = Km[55:48]; 73 | 74 | wire vsync_rising; 75 | reg vsync_v2; 76 | always @(posedge clk) begin 77 | vsync_v2 <= vsync; 78 | end 79 | assign vsync_rising = vsync & !vsync_v2; 80 | 81 | /////// 82 | // HDCP 83 | /////// 84 | // HDCP initialization procedure 85 | // 86 | // 1. Sniff An, KSV going across the wire 87 | // 2. Generate private key table for one of the KSV's 88 | // 3. Perform the Km computation using derived private key table 89 | // 4. Enter An, Km into the register for the HDCP cipher 90 | // 5. Initiate the authentication (pulse hdcpBlockCipher_init and 91 | // authentication high one cycle simultaneously) 92 | // 6. Wait until stream_ready is high 93 | // 94 | // Now begins the main loop: 95 | // There is an ambiguity in the spec. Either a rekey operation happens immediately 96 | // (since this happens during vertical retrace), or not. Either way, this is roughly 97 | // what happens. 98 | // 99 | // 1. If hdcp_ena activates (or of de and data island enable), advance cipher 100 | // 2. If vertical sync happens, pulse hdcpBlockCipher_init one cycle and wait 101 | // until stream_ready; return to 1 102 | // 3. If horizontal sync happens, pulse hdcpRekeyCipher once cycle, wait until 103 | // stream_ready; return to 1 104 | // 105 | // That's it. So the only question is if vsync "happens" immediately after an authentication. 106 | // The test vectors would suggest this is the case but I can't find it in the state machine 107 | // diagrams, so perhaps good to try both options...? 108 | parameter HDCP_UNPLUG = 18'b1 << 0; // 1 109 | parameter HDCP_WAIT_AKSV = 18'b1 << 1; // 2 110 | parameter HDCP_AUTH_PULSE = 18'b1 << 2; // 4 111 | parameter HDCP_AUTH = 18'b1 << 3; // 8 112 | parameter HDCP_AUTH_WAIT = 18'b1 << 4; // 10 113 | parameter HDCP_AUTH_VSYNC_PULSE = 18'b1 << 5; // 20 114 | parameter HDCP_AUTH_VSYNC = 18'b1 << 6; // 40 115 | parameter HDCP_AUTH_VSYNC_WAIT = 18'b1 << 7; // 80 116 | parameter HDCP_WAIT_1001 = 18'b1 << 8; // 100 117 | parameter HDCP_WAIT_1001_END = 18'b1 << 9; // 200 118 | parameter HDCP_VSYNC = 18'b1 << 10; // 400 119 | parameter HDCP_VSYNC_PULSE = 18'b1 << 11; // 800 120 | parameter HDCP_VSYNC_WAIT = 18'b1 << 12; // 1000 121 | parameter HDCP_READY = 18'b1 << 13; // 2000 122 | parameter HDCP_REKEY = 18'b1 << 14; // 4000 123 | parameter HDCP_REKEY_WAIT = 18'b1 << 15; // 8000 124 | parameter HDCP_WAIT_KMRDY = 18'b1 << 16; // 10000 125 | // parameter HDCP_REKEY_PULSE = 18'b1 << 17; // 20000 126 | 127 | parameter HDCP_nSTATES = 17; 128 | 129 | reg [(HDCP_nSTATES-1):0] HDCP_cstate = {{(HDCP_nSTATES-1){1'b0}}, 1'b1}; 130 | reg [(HDCP_nSTATES-1):0] HDCP_nstate; 131 | 132 | reg auth_mode; 133 | reg hdcp_init; 134 | wire hdcp_stream_ena; 135 | 136 | reg active_line; 137 | wire hdcp_rekey; 138 | 139 | reg hsync_v, hsync_v2; 140 | 141 | // assign hdcp_debug = HDCP_cstate; 142 | assign hdcp_is_ready = (HDCP_cstate == HDCP_READY); 143 | 144 | reg le_pipe; 145 | 146 | assign hdcp_rekey = line_end; 147 | 148 | always @ (posedge clk) begin 149 | if( rst == 1'b1 ) begin 150 | active_line <= 1'b0; 151 | hsync_v <= 1'b0; 152 | hsync_v2 <= 1'b0; 153 | end else begin 154 | hsync_v <= hsync; 155 | hsync_v2 <= hsync_v; 156 | 157 | if( de ) begin 158 | active_line <= 1'b1; 159 | end else if( !hsync_v & hsync_v2 ) begin // hsync falling 160 | active_line <= 1'b0; 161 | end 162 | end 163 | end 164 | 165 | always @ (posedge clk) begin 166 | if ( hpd | rst ) 167 | HDCP_cstate <= HDCP_UNPLUG; 168 | else 169 | if( Aksv14_write ) begin 170 | HDCP_cstate <= HDCP_AUTH_PULSE; // hack for tivo series 3 171 | end else begin 172 | HDCP_cstate <=#1 HDCP_nstate; 173 | end 174 | end 175 | 176 | always @ (*) begin 177 | case (HDCP_cstate) //synthesis parallel_case full_case 178 | HDCP_UNPLUG: begin 179 | HDCP_nstate = hpd ? HDCP_UNPLUG : HDCP_WAIT_AKSV; 180 | end 181 | HDCP_WAIT_AKSV: begin 182 | // wait until the 14th byte is written to the HDCP register set 183 | // this is the MSB of AKsv, and this triggers an authentication event 184 | HDCP_nstate = Aksv14_write ? HDCP_AUTH_PULSE : HDCP_WAIT_AKSV; 185 | // HDCP_nstate = Aksv14_write ? HDCP_WAIT_KMRDY : HDCP_WAIT_AKSV; 186 | // in this implementation, skipe the HDCP_WAIT_KMRDY state 187 | end 188 | 189 | // this state is unreachable 190 | HDCP_WAIT_KMRDY: begin 191 | HDCP_nstate = Km_ready ? HDCP_AUTH_PULSE : HDCP_WAIT_KMRDY; 192 | end 193 | 194 | //////// 195 | // maybe put a state here to wait for Km to become ready 196 | // but for now, we assume host has pre-loaded Km. Km is fixed for every Tx/Rx HDMI pair. 197 | // So once you have computed it, it can be pre-loaded even before the transaction happens. 198 | // One way around this is to snag AKsv, Bksv; and if they are a new pair, compute Km 199 | // and load it; and then override hpd high for a second to force a re-key *only* if 200 | // this is new pair. Thus, the first time you plug in a new device you *might* see it 201 | // flicker once, but it would never happen again, but I think typically you would 202 | // not notice because the screen would stay dark the entire time. 203 | // 204 | // --> above is the wait KMRDY state. The way this should work now is: 205 | // 1. Aksv is written, byte 14 triggers an interrupt to the CPU. 206 | // 2. CPU derives Km, writes Km, sets Km ready 207 | // 3. state machine then moves on to initiate auth pulse 208 | // 209 | //////// 210 | HDCP_AUTH_PULSE: begin 211 | HDCP_nstate = HDCP_AUTH; 212 | end 213 | HDCP_AUTH: begin 214 | HDCP_nstate = stream_ready? HDCP_AUTH : HDCP_AUTH_WAIT; 215 | end 216 | HDCP_AUTH_WAIT: begin 217 | HDCP_nstate = stream_ready ? HDCP_AUTH_VSYNC_PULSE : HDCP_AUTH_WAIT; 218 | end 219 | 220 | // this is a special vsync-update state just for after auth 221 | // because I don't know if there is more than 1 vsync period between 222 | // the conclusion of auth and the first 1001 assertion 223 | // if there is, then we end up unsynchronized on the Mi state 224 | HDCP_AUTH_VSYNC_PULSE: begin 225 | HDCP_nstate = HDCP_AUTH_VSYNC; 226 | end 227 | HDCP_AUTH_VSYNC: begin 228 | HDCP_nstate = stream_ready ? HDCP_AUTH_VSYNC : HDCP_AUTH_VSYNC_WAIT; 229 | end 230 | HDCP_AUTH_VSYNC_WAIT: begin 231 | HDCP_nstate = stream_ready ? HDCP_WAIT_1001 : HDCP_AUTH_VSYNC_WAIT; 232 | end 233 | 234 | // our primary wait state 235 | HDCP_WAIT_1001: begin 236 | HDCP_nstate = (vsync && (ctl_code[3:0] == 4'b1001)) ? 237 | HDCP_WAIT_1001_END : HDCP_WAIT_1001; 238 | end 239 | HDCP_WAIT_1001_END: begin 240 | HDCP_nstate = (vsync && (ctl_code[3:0] == 4'b1001)) ? 241 | HDCP_WAIT_1001_END : HDCP_READY; 242 | end 243 | 244 | 245 | HDCP_VSYNC_PULSE: begin 246 | HDCP_nstate = HDCP_VSYNC; 247 | end 248 | HDCP_VSYNC: begin 249 | HDCP_nstate = stream_ready ? HDCP_VSYNC : HDCP_VSYNC_WAIT; 250 | end 251 | HDCP_VSYNC_WAIT: begin 252 | HDCP_nstate = stream_ready ? HDCP_WAIT_1001 : HDCP_VSYNC_WAIT; 253 | end 254 | 255 | // our primary cipher state 256 | HDCP_READY: begin 257 | // Core assumption: the only way stream becomes un-ready during 258 | // HDCP_READY is due to the external rekey event. vsync_rising 259 | // will never result in this triggering because it itself must 260 | // transition this state machine to a new state before stream_ready 261 | // changes; and furthermore, stream_ready is guaranteed to be high 262 | // upon return to this state. 263 | HDCP_nstate = (stream_ready == 1'b0) ? HDCP_REKEY_WAIT : 264 | vsync_rising ? HDCP_VSYNC_PULSE : 265 | HDCP_READY; 266 | end 267 | 268 | HDCP_REKEY: begin 269 | HDCP_nstate = stream_ready ? HDCP_REKEY : HDCP_REKEY_WAIT; 270 | end 271 | HDCP_REKEY_WAIT: begin 272 | HDCP_nstate = stream_ready ? HDCP_READY : HDCP_REKEY_WAIT; 273 | end 274 | endcase // case (HDCP_cstate) 275 | end 276 | 277 | // assign Km_ready = !Km_rdy2 & Km_rdy1; // rising edge pulse 278 | assign Km_ready = Km_rdy2; // for now make it level triggered ("cheezy mode") 279 | 280 | always @ (posedge clk ) begin 281 | if( rst | hpd ) begin 282 | auth_mode <=#1 1'b0; 283 | hdcp_init <=#1 1'b0; 284 | hdcp_requested <=#1 1'b0; 285 | 286 | Km_rdy0 <= 1'b0; 287 | Km_rdy1 <= 1'b0; 288 | Km_rdy2 <= 1'b0; 289 | end else begin 290 | Km_rdy0 <= Km_valid; 291 | Km_rdy1 <= Km_rdy0; 292 | Km_rdy2 <= Km_rdy1; 293 | 294 | case (HDCP_cstate) //synthesis parallel_case full_case 295 | HDCP_UNPLUG: begin 296 | auth_mode <=#1 1'b0; 297 | hdcp_init <=#1 1'b0; 298 | hdcp_requested <=#1 1'b0; 299 | end 300 | HDCP_WAIT_AKSV: begin 301 | auth_mode <=#1 1'b0; 302 | hdcp_init <=#1 1'b0; 303 | hdcp_requested <=#1 1'b0; 304 | end 305 | 306 | HDCP_WAIT_KMRDY: begin 307 | auth_mode <=#1 1'b0; 308 | hdcp_init <=#1 1'b0; 309 | hdcp_requested <=#1 1'b0; 310 | end 311 | 312 | HDCP_AUTH_PULSE: begin 313 | auth_mode <=#1 1'b1; 314 | hdcp_init <=#1 1'b1; // pulse just one cycle 315 | hdcp_requested <=#1 1'b0; 316 | end 317 | HDCP_AUTH: begin 318 | auth_mode <=#1 1'b0; 319 | hdcp_init <=#1 1'b0; 320 | hdcp_requested <=#1 1'b0; 321 | end 322 | HDCP_AUTH_WAIT: begin 323 | auth_mode <=#1 1'b0; 324 | hdcp_init <=#1 1'b0; 325 | hdcp_requested <=#1 1'b0; 326 | end 327 | 328 | HDCP_AUTH_VSYNC_PULSE: begin 329 | auth_mode <=#1 1'b0; 330 | hdcp_init <=#1 1'b1; // pulse init, but not with auth_mode 331 | hdcp_requested <=#1 1'b0; 332 | end 333 | HDCP_AUTH_VSYNC: begin 334 | auth_mode <=#1 1'b0; 335 | hdcp_init <=#1 1'b0; 336 | hdcp_requested <=#1 1'b0; 337 | end 338 | HDCP_AUTH_VSYNC_WAIT: begin 339 | auth_mode <=#1 1'b0; 340 | hdcp_init <=#1 1'b0; 341 | hdcp_requested <=#1 1'b0; 342 | end 343 | 344 | HDCP_WAIT_1001: begin 345 | auth_mode <=#1 1'b0; 346 | hdcp_init <=#1 1'b0; 347 | hdcp_requested <=#1 1'b0; 348 | end 349 | HDCP_WAIT_1001_END: begin 350 | auth_mode <=#1 1'b0; 351 | hdcp_init <=#1 1'b0; 352 | hdcp_requested <=#1 1'b0; 353 | end 354 | 355 | HDCP_VSYNC_PULSE: begin 356 | auth_mode <=#1 1'b0; 357 | hdcp_init <=#1 1'b1; // pulse init, but not with auth_mode 358 | hdcp_requested <=#1 1'b1; 359 | end 360 | HDCP_VSYNC: begin 361 | auth_mode <=#1 1'b0; 362 | hdcp_init <=#1 1'b0; 363 | hdcp_requested <=#1 1'b1; 364 | end 365 | HDCP_VSYNC_WAIT: begin 366 | auth_mode <=#1 1'b0; 367 | hdcp_init <=#1 1'b0; 368 | hdcp_requested <=#1 1'b1; 369 | end 370 | 371 | HDCP_READY: begin 372 | auth_mode <=#1 1'b0; 373 | hdcp_init <=#1 1'b0; 374 | hdcp_requested <=#1 1'b1; 375 | end 376 | 377 | HDCP_REKEY: begin 378 | auth_mode <=#1 1'b0; 379 | hdcp_init <=#1 1'b0; 380 | hdcp_requested <=#1 1'b1; 381 | end 382 | HDCP_REKEY_WAIT: begin 383 | auth_mode <=#1 1'b0; 384 | hdcp_init <=#1 1'b0; 385 | hdcp_requested <=#1 1'b1; 386 | end 387 | endcase // case (HDCP_cstate) 388 | end // else: !if( ~rstbtn_n | hpd ) 389 | end // always @ (posedge tx0_pclk) 390 | 391 | 392 | wire stream_ready; 393 | hdcp_cipher cipher ( 394 | .clk(clk), 395 | .reset(rst), 396 | .Km(Km), 397 | .An(An), 398 | .hdcpBlockCipher_init(hdcp_init), 399 | .authentication(auth_mode), 400 | .hdcpRekeyCipher(hdcp_rekey), 401 | .hdcpStreamCipher(hdcp_ena && (HDCP_cstate == HDCP_READY)), 402 | .pr_data(cipher_stream), 403 | .stream_ready(stream_ready) 404 | // .cipher_debug(cipher_debug) 405 | ); 406 | endmodule // hdcp_mod 407 | -------------------------------------------------------------------------------- /overlay/hdcp_cipher.v: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2011, Andrew "bunnie" Huang 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, 9 | // this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above copyright notice, 11 | // this list of conditions and the following disclaimer in the documentation and/or 12 | // other materials provided with the distribution. 13 | // 14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 21 | // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | // POSSIBILITY OF SUCH DAMAGE. 24 | // 25 | ////////////////////////////////////////////////////////////////////////////// 26 | 27 | `timescale 1 ns / 1 ps 28 | 29 | // generates a stream of hdcp cipher data 30 | module hdcp_cipher( 31 | input wire clk, 32 | input reset, 33 | 34 | input [55:0] Km, // shared secret value 35 | input [63:0] An, // session random number 36 | 37 | input hdcpBlockCipher_init, // pulsed only one cycle 38 | input authentication, // pulsed one cycle same time as above 39 | input hdcpRekeyCipher, // pulsed one cycle to initiate rekey 40 | input hdcpStreamCipher,// advance cipher state one clock 41 | output [23:0] pr_data, // pseudorandom data output 42 | output reg stream_ready // asserted when stream is ready (after init seq) 43 | // output [12:0] cipher_debug 44 | ); 45 | 46 | wire lfsr_out; 47 | wire [23:0] ostream; 48 | wire [83:0] Bo_wire; 49 | 50 | parameter INIT = 12'b1 << 0; // 1 51 | parameter BLOCK_1 = 12'b1 << 1; // 2 52 | parameter BLOCK_2 = 12'b1 << 2; // 4 53 | parameter BLOCK_3 = 12'b1 << 3; // 8 54 | parameter BLOCK_4 = 12'b1 << 4; // 10 55 | parameter BLOCK_5 = 12'b1 << 5; // 20 56 | parameter BLOCK_6 = 12'b1 << 6; // 40 57 | parameter BLOCK_7 = 12'b1 << 7; // 80 58 | parameter BLOCK_8 = 12'b1 << 8; // 100 59 | parameter BLOCK_9 = 12'b1 << 9; // 200 60 | parameter GET_M = 12'b1 << 10; // 400 61 | parameter STREAM = 12'b1 << 11; // 800 62 | parameter REKEY = 12'b1 << 12; // 1000 (optimized to 0) 63 | parameter nSTATES = 13; 64 | 65 | reg [(nSTATES-1):0] cstate = {{(nSTATES-1){1'b0}},1'b1}; 66 | reg [(nSTATES-1):0] nstate; 67 | 68 | // assign cipher_debug = cstate; 69 | 70 | // `define SIMULATION 1 71 | `ifdef SIMULATION 72 | // synthesis translate_off 73 | reg [8*20:1] state_ascii = "INIT "; 74 | always @(cstate) begin 75 | if (cstate == INIT ) state_ascii <= "INIT "; 76 | else if (cstate == BLOCK_1 ) state_ascii <= "BLOCK_1 "; 77 | else if (cstate == BLOCK_2 ) state_ascii <= "BLOCK_2 "; 78 | else if (cstate == BLOCK_3 ) state_ascii <= "BLOCK_3 "; 79 | else if (cstate == BLOCK_4 ) state_ascii <= "BLOCK_4 "; 80 | else if (cstate == BLOCK_5 ) state_ascii <= "BLOCK_5 "; 81 | else if (cstate == BLOCK_6 ) state_ascii <= "BLOCK_6 "; 82 | else if (cstate == BLOCK_7 ) state_ascii <= "BLOCK_7 "; 83 | else if (cstate == BLOCK_8 ) state_ascii <= "BLOCK_8 "; 84 | else if (cstate == BLOCK_9 ) state_ascii <= "BLOCK_9 "; 85 | else if (cstate == GET_M ) state_ascii <= "GET_M "; 86 | else if (cstate == STREAM ) state_ascii <= "STREAM "; 87 | else if (cstate == REKEY ) state_ascii <= "REKEY "; 88 | else state_ascii <= "WTF "; 89 | end 90 | // synthesis translate_on 91 | `endif // `ifdef SIMULATION 92 | 93 | reg [5:0] statecnt; 94 | reg rekey; 95 | reg load_block; 96 | reg load_56; // load 56 or 80 bits... 97 | reg load_lfsr; 98 | reg advance_lfsr; 99 | reg advance_block; 100 | reg [83:0] Ks; 101 | reg [83:0] Ki; 102 | reg [63:0] Mi; 103 | reg load_ks; 104 | reg auth_mode; 105 | reg [83:0] Kmod; 106 | 107 | always @ (posedge clk or posedge reset) begin 108 | if (reset) 109 | cstate <= INIT; 110 | else 111 | cstate <=#1 nstate; 112 | end 113 | 114 | hdcp_lfsr lfsr( .clk(clk), .reset(reset), 115 | .iv(auth_mode ? Ks[55:0] : Ki[55:0]), 116 | .init_iv(load_lfsr), 117 | .advance(advance_lfsr), 118 | .onebit(lfsr_out)); 119 | 120 | hdcp_block block( .clk(clk), .reset(reset), 121 | 122 | .load(load_block), 123 | .B(auth_mode ? {20'b0,An} : {20'b0,Mi}), 124 | .K(Kmod), 125 | .Bo(Bo_wire), 126 | .rekey(rekey), 127 | .lfsr_in(lfsr_out), 128 | .ostream(ostream), 129 | .advance(advance_block)); 130 | assign pr_data = ostream; 131 | 132 | always @ (*) begin 133 | case ({auth_mode,load_56}) //synthesis parallel_case full_case 134 | 2'b00: begin // not auth mode, load 84 bits 135 | Kmod = Ki; 136 | end 137 | 2'b01: begin // not auth mode, but load 56 bits only 138 | Kmod = {28'b0,Ks[55:0]}; 139 | end 140 | 2'b10: begin // auth mode, load 84 bits 141 | Kmod = Ks; 142 | end 143 | 2'b11: begin // auth mode, load only 56 bits 144 | Kmod = {28'b0,Km[55:0]}; 145 | end 146 | endcase // case ({auth_mode,load_56}) 147 | end // always @ (*) 148 | 149 | 150 | always @ (*) begin 151 | case (cstate) //synthesis parallel_case full_case 152 | INIT: begin 153 | nstate = hdcpBlockCipher_init ? BLOCK_1 : INIT; 154 | end 155 | BLOCK_1: begin 156 | nstate = BLOCK_2; 157 | end 158 | BLOCK_2: begin 159 | nstate = (statecnt >= 6'd47) ? BLOCK_3: BLOCK_2; 160 | end 161 | BLOCK_3: begin 162 | nstate = BLOCK_4; 163 | end 164 | BLOCK_4: begin 165 | nstate = BLOCK_5; 166 | end 167 | BLOCK_5: begin 168 | nstate = BLOCK_6; 169 | end 170 | BLOCK_6: begin 171 | nstate = BLOCK_7; 172 | end 173 | BLOCK_7: begin 174 | nstate = BLOCK_8; 175 | end 176 | BLOCK_8: begin 177 | nstate = (statecnt >= 6'd55) ? BLOCK_9: BLOCK_8; 178 | end 179 | BLOCK_9: begin 180 | nstate = GET_M; 181 | end 182 | GET_M: begin 183 | nstate = STREAM; 184 | end 185 | STREAM: begin 186 | if( hdcpBlockCipher_init ) begin 187 | nstate = BLOCK_1; 188 | end else if( hdcpRekeyCipher ) begin 189 | nstate = REKEY; 190 | end else begin 191 | nstate = STREAM; 192 | end 193 | end 194 | REKEY: begin 195 | if( hdcpBlockCipher_init ) begin 196 | nstate = BLOCK_1; 197 | end else begin 198 | nstate = (statecnt >= 6'd55) ? STREAM : REKEY; 199 | end 200 | end 201 | endcase // case (cstate) 202 | end 203 | 204 | always @ (posedge clk or posedge reset) begin 205 | if( reset ) begin 206 | statecnt <=#1 6'b0; 207 | rekey <=#1 1'b0; 208 | load_block <=#1 1'b0; 209 | advance_lfsr <=#1 1'b0; 210 | advance_block <=#1 1'b0; 211 | load_ks <=#1 1'b0; 212 | auth_mode <=#1 1'b0; 213 | stream_ready <=#1 1'b0; 214 | load_56 <=#1 1'b0; 215 | end else begin 216 | case (cstate) // synthesis parallel_case full_case 217 | INIT: begin 218 | statecnt <=#1 6'b0; 219 | rekey <=#1 1'b0; 220 | load_block <=#1 1'b0; 221 | advance_lfsr <=#1 1'b0; 222 | advance_block <=#1 1'b0; 223 | load_ks <=#1 1'b0; 224 | auth_mode <=#1 authentication; 225 | load_lfsr <=#1 1'b0; 226 | stream_ready <=#1 1'b0; 227 | load_56 <=#1 1'b0; 228 | end 229 | BLOCK_1: begin 230 | statecnt <=#1 6'b0; 231 | rekey <=#1 1'b0; 232 | load_block <=#1 1'b1; // load B & K regs of block module 233 | advance_lfsr <=#1 1'b0; 234 | advance_block <=#1 1'b0; 235 | load_ks <=#1 1'b0; 236 | auth_mode <=#1 auth_mode; 237 | load_lfsr <=#1 1'b0; 238 | stream_ready <=#1 1'b0; 239 | load_56 <=#1 1'b1; 240 | end 241 | BLOCK_2: begin 242 | statecnt <=#1 statecnt + 1; // 48 clocks 243 | rekey <=#1 1'b0; 244 | load_block <=#1 1'b0; 245 | advance_lfsr <=#1 1'b0; 246 | advance_block <=#1 1'b1; 247 | load_ks <=#1 1'b0; 248 | auth_mode <=#1 auth_mode; 249 | load_lfsr <=#1 1'b0; 250 | stream_ready <=#1 1'b0; 251 | load_56 <=#1 1'b0; 252 | end 253 | BLOCK_3: begin 254 | statecnt <=#1 0; 255 | rekey <=#1 1'b0; 256 | load_block <=#1 1'b0; 257 | advance_lfsr <=#1 1'b0; 258 | advance_block <=#1 1'b0; 259 | load_ks <=#1 1'b1; // save Ks, Ki, and B=>K 260 | auth_mode <=#1 auth_mode; 261 | load_lfsr <=#1 1'b0; 262 | stream_ready <=#1 1'b0; 263 | load_56 <=#1 1'b0; 264 | end 265 | BLOCK_4: begin 266 | statecnt <=#1 0; 267 | rekey <=#1 1'b0; 268 | load_block <=#1 1'b0; 269 | advance_lfsr <=#1 1'b0; 270 | advance_block <=#1 1'b0; 271 | load_ks <=#1 1'b1; // dup of above 272 | auth_mode <=#1 auth_mode; 273 | load_lfsr <=#1 1'b0; 274 | stream_ready <=#1 1'b0; 275 | load_56 <=#1 1'b0; 276 | end 277 | BLOCK_5: begin 278 | statecnt <=#1 0; 279 | rekey <=#1 1'b0; 280 | load_block <=#1 1'b1; // reload block cipher 281 | advance_lfsr <=#1 1'b0; 282 | advance_block <=#1 1'b0; 283 | load_ks <=#1 1'b0; 284 | auth_mode <=#1 auth_mode; 285 | load_lfsr <=#1 1'b0; 286 | stream_ready <=#1 1'b0; 287 | load_56 <=#1 1'b0; 288 | end 289 | BLOCK_6: begin 290 | statecnt <=#1 0; 291 | rekey <=#1 1'b0; 292 | load_block <=#1 1'b0; 293 | advance_lfsr <=#1 1'b0; 294 | advance_block <=#1 1'b0; 295 | load_ks <=#1 1'b0; 296 | auth_mode <=#1 auth_mode; 297 | load_lfsr <=#1 1'b1; // init lfsr 298 | stream_ready <=#1 1'b0; 299 | load_56 <=#1 1'b0; 300 | end 301 | BLOCK_7: begin 302 | statecnt <=#1 0; 303 | rekey <=#1 1'b1; // assert rekey 304 | load_block <=#1 1'b0; 305 | advance_lfsr <=#1 1'b0; 306 | advance_block <=#1 1'b0; 307 | load_ks <=#1 1'b0; 308 | auth_mode <=#1 auth_mode; 309 | load_lfsr <=#1 1'b0; 310 | stream_ready <=#1 1'b0; 311 | load_56 <=#1 1'b0; 312 | end 313 | BLOCK_8: begin 314 | statecnt <=#1 statecnt + 1; // 56 clocks 315 | rekey <=#1 1'b1; 316 | load_block <=#1 1'b0; 317 | advance_lfsr <=#1 1'b1; 318 | advance_block <=#1 1'b1; 319 | load_ks <=#1 1'b0; 320 | auth_mode <=#1 auth_mode; 321 | load_lfsr <=#1 1'b0; 322 | stream_ready <=#1 1'b0; 323 | load_56 <=#1 1'b0; 324 | end 325 | BLOCK_9: begin 326 | statecnt <=#1 0; 327 | rekey <=#1 1'b0; // de-assert rekey 328 | load_block <=#1 1'b0; 329 | advance_lfsr <=#1 1'b0; 330 | advance_block <=#1 1'b0; 331 | load_ks <=#1 1'b0; 332 | auth_mode <=#1 auth_mode; 333 | load_lfsr <=#1 1'b0; 334 | stream_ready <=#1 1'b0; 335 | load_56 <=#1 1'b0; 336 | end // case: BLOCK_9 337 | GET_M: begin // one cycle wait to get M register loaded properly 338 | statecnt <=#1 0; 339 | rekey <=#1 1'b0; 340 | load_block <=#1 1'b0; 341 | advance_lfsr <=#1 1'b0; 342 | advance_block <=#1 1'b0; 343 | load_ks <=#1 1'b0; 344 | auth_mode <=#1 1'b0; 345 | load_lfsr <=#1 1'b0; 346 | stream_ready <=#1 1'b0; 347 | load_56 <=#1 1'b0; 348 | end 349 | STREAM: begin 350 | if( !hdcpBlockCipher_init && hdcpRekeyCipher ) begin 351 | // start the rekey immediately to meet timing requirements (copy of REKEY state here) 352 | statecnt <=#1 statecnt + 1; 353 | rekey <=#1 1'b1; 354 | load_block <=#1 1'b0; 355 | advance_lfsr <=#1 1'b1; 356 | advance_block <=#1 1'b1; 357 | load_ks <=#1 1'b0; 358 | auth_mode <=#1 auth_mode; 359 | load_lfsr <=#1 1'b0; 360 | stream_ready <=#1 1'b0; 361 | load_56 <=#1 1'b0; 362 | end else begin // if ( !hdcpBlockCipher_init && hdcpRekeyCipher ) 363 | // default stream state 364 | statecnt <=#1 0; 365 | rekey <=#1 1'b0; 366 | load_block <=#1 1'b0; 367 | advance_lfsr <=#1 hdcpStreamCipher; 368 | advance_block <=#1 hdcpStreamCipher; 369 | load_ks <=#1 1'b0; 370 | auth_mode <=#1 authentication; 371 | load_lfsr <=#1 1'b0; 372 | stream_ready <=#1 1'b1; 373 | load_56 <=#1 1'b0; 374 | end 375 | end 376 | REKEY: begin 377 | statecnt <=#1 statecnt + 1; 378 | rekey <=#1 1'b1; 379 | load_block <=#1 1'b0; 380 | advance_lfsr <=#1 1'b1; 381 | advance_block <=#1 1'b1; 382 | load_ks <=#1 1'b0; 383 | auth_mode <=#1 auth_mode; 384 | load_lfsr <=#1 1'b0; 385 | stream_ready <=#1 1'b0; 386 | load_56 <=#1 1'b0; 387 | end 388 | endcase // case (cstate) 389 | end 390 | end // always @ (posedge clk or posedge reset) 391 | 392 | always @(posedge clk or posedge reset) begin 393 | if( reset ) begin 394 | Ks <= 80'b0; 395 | Ki <= 80'b0; 396 | end else begin 397 | // if( hdcpBlockCipher_init ) begin 398 | // Ks <= (authentication | auth_mode) ? {28'b0,Km} : Ks; 399 | // Ki <= 80'b0; 400 | // end else if( load_ks && auth_mode ) begin 401 | if( load_ks && auth_mode ) begin 402 | Ks <= Bo_wire; 403 | Ki <= 80'b0; 404 | end else if( load_ks && !auth_mode ) begin 405 | Ks <= Ks; 406 | Ki <= Bo_wire; 407 | end else begin 408 | Ks <= Ks; 409 | Ki <= Ki; 410 | end 411 | end 412 | end // always @ (posedge clk or posedge reset) 413 | 414 | always @(posedge clk or posedge reset) begin 415 | if( reset ) begin 416 | Mi <= 80'b0; 417 | end else begin 418 | if( (cstate == BLOCK_8) || (cstate == BLOCK_9) || (cstate == GET_M) ) begin 419 | Mi[15:0] <= ostream[15:0]; 420 | Mi[31:16] <= Mi[15:0]; 421 | Mi[47:32] <= Mi[31:16]; 422 | Mi[63:48] <= Mi[47:32]; 423 | end else begin 424 | Mi <= Mi; 425 | end 426 | end // else: !if( reset ) 427 | end // always @ (posedge clk or posedge reset) 428 | 429 | endmodule // hdcp_cipher 430 | -------------------------------------------------------------------------------- /lxbuildenv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script enables easy, cross-platform building without the need 4 | # to install third-party Python modules. 5 | 6 | import sys 7 | import os 8 | import subprocess 9 | import argparse 10 | 11 | 12 | DEPS_DIR = "deps" 13 | 14 | # Obtain the path to this script, plus a trailing separator. This will 15 | # be used later on to construct various environment variables for paths 16 | # to a variety of support directories. 17 | script_path = os.path.dirname(os.path.realpath(__file__)) + os.path.sep 18 | 19 | # Look through the specified file for known variables to get the dependency list 20 | def get_required_dependencies(filename): 21 | import ast 22 | 23 | # Always check the Python version 24 | dependencies = { 25 | 'python': 1 26 | } 27 | main_src = "" 28 | 29 | try: 30 | with open(sys.argv[0], 'r') as f: 31 | main_src = f.read() 32 | main_ast = ast.parse(main_src, filename=filename) 33 | except: 34 | return list(dependencies.keys()) 35 | 36 | # Iterate through the top-level nodes looking for variables named 37 | # LX_DEPENDENCIES or LX_DEPENDENCY and get the values that are 38 | # assigned to them. 39 | for node in ast.iter_child_nodes(main_ast): 40 | if isinstance(node, ast.Assign): 41 | value = node.value 42 | for target in node.targets: 43 | if isinstance(target, ast.Name): 44 | if target.id == "LX_DEPENDENCIES" or target.id == "LX_DEPENDENCY": 45 | if isinstance(value, (ast.List, ast.Tuple)): 46 | for elt in value.elts: 47 | if isinstance(elt, ast.Str): 48 | dependencies[elt.s] = 1 49 | elif isinstance(value, ast.Str): 50 | dependencies[value.s] = 1 51 | 52 | # Set up sub-dependencies 53 | if 'riscv' in dependencies: 54 | dependencies['make'] = 1 55 | return list(dependencies.keys()) 56 | 57 | def get_python_path(script_path, args): 58 | # Python has no concept of a local dependency path, such as the C `-I`` 59 | # switch, or the nodejs `node_modules` path, or the rust cargo registry. 60 | # Instead, it relies on an environment variable to append to the search 61 | # path. 62 | # Construct this variable by adding each subdirectory under the `deps/` 63 | # directory to the PYTHONPATH environment variable. 64 | python_path = [] 65 | if os.path.isdir(script_path + DEPS_DIR): 66 | for dep in os.listdir(script_path + DEPS_DIR): 67 | dep = script_path + DEPS_DIR + os.path.sep + dep 68 | if os.path.isdir(dep): 69 | python_path.append(dep) 70 | return python_path 71 | 72 | def fixup_env(script_path, args): 73 | os.environ["PYTHONPATH"] = os.pathsep.join(get_python_path(script_path, 0)) 74 | 75 | # Set the "LXBUILDENV_REEXEC" variable to prevent the script from continuously 76 | # reinvoking itself. 77 | os.environ["LXBUILDENV_REEXEC"] = "1" 78 | 79 | # Python randomizes the order in which it traverses hashes, and Migen uses 80 | # hashes an awful lot when bringing together modules. As such, the order 81 | # in which Migen generates its output Verilog will change with every run, 82 | # and the addresses for various modules will change. 83 | # Make builds deterministic so that the generated Verilog code won't change 84 | # across runs. 85 | os.environ["PYTHONHASHSEED"] = "1" 86 | 87 | # Some Makefiles are invoked as part of the build process, and those Makefiles 88 | # occasionally have calls to Python. Ensure those Makefiles use the same 89 | # interpreter that this script is using. 90 | os.environ["PYTHON"] = sys.executable 91 | 92 | # Set the environment variable "V" to 1. This causes Makefiles to print 93 | # the commands they run, which makes them easier to debug. 94 | if args.lx_verbose: 95 | os.environ["V"] = "1" 96 | 97 | # If the user just wanted to print the environment variables, do that and quit. 98 | if args.lx_print_env: 99 | print("PYTHONPATH={}".format(os.environ["PYTHONPATH"])) 100 | print("PYTHONHASHSEED={}".format(os.environ["PYTHONHASHSEED"])) 101 | print("PYTHON={}".format(sys.executable)) 102 | print("LXBUILDENV_REEXEC={}".format(os.environ["LXBUILDENV_REEXEC"])) 103 | 104 | sys.exit(0) 105 | 106 | # Equivalent to the powershell Get-Command, and kinda like `which` 107 | def get_command(cmd): 108 | if os.name == 'nt': 109 | path_ext = os.environ["PATHEXT"].split(os.pathsep) 110 | else: 111 | path_ext = [""] 112 | for ext in path_ext: 113 | for path in os.environ["PATH"].split(os.pathsep): 114 | 115 | if os.path.exists(path + os.path.sep + cmd + ext): 116 | return path + os.path.sep + cmd + ext 117 | return None 118 | 119 | def check_python_version(args): 120 | import platform 121 | # Litex / Migen require Python 3.5 or newer. Ensure we're running 122 | # under a compatible version of Python. 123 | if sys.version_info[:3] < (3, 5): 124 | return (False, 125 | "python: You need Python 3.5+ (version {} found)".format(sys.version_info[:3])) 126 | return (True, "python 3.5+: ok (Python {} found)".format(platform.python_version())) 127 | 128 | def check_vivado(args): 129 | vivado_path = get_command("vivado") 130 | if vivado_path == None: 131 | # Look for the default Vivado install directory 132 | if os.name == 'nt': 133 | base_dir = r"C:\Xilinx\Vivado" 134 | else: 135 | base_dir = "/opt/Xilinx/Vivado" 136 | if os.path.exists(base_dir): 137 | for file in os.listdir(base_dir): 138 | bin_dir = base_dir + os.path.sep + file + os.path.sep + "bin" 139 | if os.path.exists(bin_dir + os.path.sep + "vivado"): 140 | os.environ["PATH"] += os.pathsep + bin_dir 141 | vivado_path = bin_dir 142 | break 143 | if vivado_path == None: 144 | return (False, "toolchain not found in your PATH", "download it from https://www.xilinx.com/support/download.html") 145 | return (True, "found at {}".format(vivado_path)) 146 | 147 | def check_cmd(args, cmd, name=None, fix=None): 148 | if name is None: 149 | name = cmd 150 | path = get_command(cmd) 151 | if path == None: 152 | return (False, name + " not found in your PATH", fix) 153 | return (True, "found at {}".format(path)) 154 | 155 | def check_make(args): 156 | return check_cmd(args, "make", "GNU Make") 157 | 158 | def check_riscv(args): 159 | return check_cmd(args, "riscv64-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/products/tools/") 160 | 161 | def check_yosys(args): 162 | return check_cmd(args, "yosys") 163 | 164 | def check_arachne(args): 165 | return check_cmd(args, "arachne-pnr") 166 | 167 | dependency_checkers = { 168 | 'python': check_python_version, 169 | 'vivado': check_vivado, 170 | 'make': check_make, 171 | 'riscv': check_riscv, 172 | 'yosys': check_yosys, 173 | 'arachne-pnr': check_arachne, 174 | } 175 | 176 | # Validate that the required dependencies (Vivado, compilers, etc.) 177 | # have been installed. 178 | def check_dependencies(args, dependency_list): 179 | 180 | dependency_errors = 0 181 | for dependency_name in dependency_list: 182 | if not dependency_name in dependency_checkers: 183 | print('WARNING: Unrecognized dependency "{}"'.format(dependency_name)) 184 | continue 185 | result = dependency_checkers[dependency_name](args) 186 | if result[0] == False: 187 | if len(result) > 2: 188 | print('{}: {} -- {}'.format(dependency_name, result[1], result[2])) 189 | else: 190 | print('{}: {}'.format(dependency_name, result[1])) 191 | dependency_errors = dependency_errors + 1 192 | 193 | elif args.lx_check_deps or args.lx_verbose: 194 | print('dependency: {}: {}'.format(dependency_name, result[1])) 195 | if dependency_errors > 0: 196 | if args.lx_ignore_deps: 197 | print('{} missing dependencies were found but continuing anyway'.format(dependency_errors)) 198 | else: 199 | raise SystemExit(str(dependency_errors) + 200 | " missing dependencies were found") 201 | 202 | if args.lx_check_deps: 203 | sys.exit(0) 204 | 205 | # Return True if the given tree needs to be initialized 206 | def check_module_recursive(root_path, depth, verbose=False): 207 | if verbose: 208 | print('git-dep: checking if "{}" requires updating...'.format(root_path)) 209 | # If the directory isn't a valid git repo, initialization is required 210 | if not os.path.exists(root_path + os.path.sep + '.git'): 211 | return True 212 | 213 | # If there are no submodules, no initialization needs to be done 214 | if not os.path.isfile(root_path + os.path.sep + '.gitmodules'): 215 | return False 216 | 217 | # Loop through the gitmodules to check all submodules 218 | gitmodules = open(root_path + os.path.sep + '.gitmodules', 'r') 219 | for line in gitmodules: 220 | parts = line.split("=", 2) 221 | if parts[0].strip() == "path": 222 | path = parts[1].strip() 223 | if check_module_recursive(root_path + os.path.sep + path, depth + 1, verbose=verbose): 224 | return True 225 | return False 226 | 227 | # Determine whether we need to invoke "git submodules init --recurse" 228 | def check_submodules(script_path, args): 229 | if check_module_recursive(script_path, 0, verbose=args.lx_verbose): 230 | print("Missing submodules -- updating") 231 | subprocess.Popen(["git", "submodule", "update", 232 | "--init", "--recursive"], cwd=script_path).wait() 233 | elif args.lx_verbose: 234 | print("Submodule check: Submodules found") 235 | 236 | 237 | def main(args): 238 | if args.init: 239 | main_name = os.getcwd().split(os.path.sep)[-1] + '.py' 240 | new_main_name = input('What would you like your main program to be called? [' + main_name + '] ') 241 | if new_main_name is not None and new_main_name != "": 242 | main_name = new_main_name 243 | 244 | print("Initializing git repository") 245 | if not os.path.exists(DEPS_DIR): 246 | os.mkdir(DEPS_DIR) 247 | 248 | os.system("git init") 249 | os.system("git add " + str(__file__)) 250 | 251 | os.system("git submodule add https://github.com/m-labs/migen.git deps/migen") 252 | os.system("git add deps/migen") 253 | 254 | os.system("git submodule add https://github.com/enjoy-digital/litex.git deps/litex") 255 | os.system("git add deps/litex") 256 | 257 | os.system("git submodule add https://github.com/enjoy-digital/litescope deps/litescope") 258 | os.system("git add deps/litescope") 259 | 260 | os.system("git submodule add https://github.com/pyserial/pyserial.git deps/pyserial") 261 | os.system("git add deps/pyserial") 262 | 263 | os.system("git submodule update --init --recursive") 264 | 265 | bin_tools = { 266 | 'litex_server': 'litex.soc.tools.remote.litex_server', 267 | 'litex_term': 'litex.soc.tools.litex_term', 268 | 'mkmscimg': 'litex.soc.tools.mkmscimg', 269 | } 270 | bin_template = """ 271 | #!/usr/bin/env python3 272 | 273 | import sys 274 | import os 275 | 276 | # This script lives in the "bin" directory, but uses a helper script in the parent 277 | # directory. Obtain the current path so we can get the absolute parent path. 278 | script_path = os.path.dirname(os.path.realpath( 279 | __file__)) + os.path.sep + os.path.pardir + os.path.sep 280 | sys.path.insert(0, script_path)it 281 | import lxbuildenv 282 | 283 | from litex.soc.tools.mkmscimg import main 284 | main()""" 285 | # Create binary programs under bin/ 286 | if not os.path.exists("bin"): 287 | print("Creating binaries") 288 | os.mkdir("bin") 289 | for bin_name, python_module in bin_tools.items(): 290 | with open('bin' + os.path.sep + bin_name, 'w') as new_bin: 291 | new_bin.write(bin_template) 292 | new_bin.write('from ' + python_module + ' import main\n') 293 | new_bin.write('main()\n') 294 | os.system('git add --chmod=+x bin' + os.path.sep + bin_name) 295 | 296 | with open(main_name, 'w') as m: 297 | program_template = """#!/usr/bin/env python3 298 | # This variable defines all the external programs that this module 299 | # relies on. lxbuildenv reads this variable in order to ensure 300 | # the build will finish without exiting due to missing third-party 301 | # programs. 302 | LX_DEPENDENCIES = ["riscv", "vivado"] 303 | 304 | # Import lxbuildenv to integrate the deps/ directory 305 | import lxbuildenv 306 | 307 | from migen import * 308 | from litex.build.generic_platform import * 309 | 310 | _io = [ 311 | ("clk50", 0, Pins("J19"), IOStandard("LVCMOS33")), 312 | ] 313 | 314 | class Platform(XilinxPlatform): 315 | def __init__(self, toolchain="vivado", programmer="vivado", part="35"): 316 | part = "xc7a" + part + "t-fgg484-2" 317 | def create_programmer(self): 318 | if self.programmer == "vivado": 319 | return VivadoProgrammer(flash_part="n25q128-3.3v-spi-x1_x2_x4") 320 | else: 321 | raise ValueError("{} programmer is not supported" 322 | .format(self.programmer)) 323 | 324 | def do_finalize(self, fragment): 325 | XilinxPlatform.do_finalize(self, fragment) 326 | 327 | class BaseSoC(SoCSDRAM): 328 | csr_peripherals = [ 329 | "ddrphy", 330 | # "dna", 331 | "xadc", 332 | "cpu_or_bridge", 333 | ] 334 | csr_map_update(SoCSDRAM.csr_map, csr_peripherals) 335 | 336 | def __init__(self, platform, **kwargs): 337 | clk_freq = int(100e6) 338 | 339 | def main(): 340 | platform = Platform() 341 | soc = BaseSoC(platform) 342 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 343 | vns = builder.build() 344 | soc.do_exit(vns) 345 | 346 | if __name__ == "__main__": 347 | main() 348 | """ 349 | m.write(program_template) 350 | return 351 | 352 | # For the main command, parse args and hand it off to main() 353 | if __name__ == "__main__": 354 | parser = argparse.ArgumentParser( 355 | description="Wrap Python code to enable quickstart", 356 | add_help=False) 357 | parser.add_argument( 358 | "-h", "--help", help="show this help message and exit", action="help" 359 | ) 360 | parser.add_argument( 361 | '-i', '--init', help='initialize a new project', action="store_true" 362 | ) 363 | args = parser.parse_args() 364 | 365 | main(args) 366 | 367 | elif not os.path.isfile(sys.argv[0]): 368 | print("lxbuildenv doesn't operate while in interactive mode") 369 | 370 | elif "LXBUILDENV_REEXEC" not in os.environ: 371 | parser = argparse.ArgumentParser( 372 | description="Wrap Python code to enable quickstart", 373 | add_help=False) 374 | parser.add_argument( 375 | "--lx-verbose", help="increase verboseness of some processes", action="store_true" 376 | ) 377 | parser.add_argument( 378 | "--lx-print-env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true" 379 | ) 380 | parser.add_argument( 381 | "--lx-check-deps", help="check build environment for dependencies such as compiler and fpga tools and then exit", action="store_true" 382 | ) 383 | parser.add_argument( 384 | "--lx-all-deps", help="print all possible dependencies and then exit", action="store_true" 385 | ) 386 | parser.add_argument( 387 | "--lx-help", action="help" 388 | ) 389 | parser.add_argument( 390 | "--lx-ignore-deps", help="try building even if dependencies are missing", action="store_true" 391 | ) 392 | (args, rest) = parser.parse_known_args() 393 | 394 | if args.lx_all_deps: 395 | print('Known dependencies:') 396 | for dep in dependency_checkers.keys(): 397 | print(' {}'.format(dep)) 398 | print('To define a dependency, add a variable inside {} at the top level called LX_DEPENDENCIES and assign it a list or tuple.'.format(sys.argv[0])) 399 | print('For example:') 400 | print('LX_DEPENDENCIES = ("riscv", "vivado")') 401 | sys.exit(0) 402 | 403 | deps = get_required_dependencies(sys.argv[0]) 404 | 405 | fixup_env(script_path, args) 406 | check_dependencies(args, deps) 407 | check_submodules(script_path, args) 408 | 409 | try: 410 | sys.exit(subprocess.Popen( 411 | [sys.executable] + [sys.argv[0]] + rest).wait()) 412 | except: 413 | sys.exit(1) 414 | else: 415 | # Overwrite the deps directory. 416 | # Because we're running with a predefined PYTHONPATH, you'd think that 417 | # the DEPS_DIR would be first. 418 | # Unfortunately, setuptools causes the sitewide packages to take precedence 419 | # over the PYTHONPATH variable. 420 | # Work around this bug by inserting paths into the first index. 421 | for path in get_python_path(script_path, None): 422 | sys.path.insert(0, path) 423 | -------------------------------------------------------------------------------- /sim/lxbuildenv_sim.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script enables easy, cross-platform building without the need 4 | # to install third-party Python modules. 5 | 6 | import sys 7 | import os 8 | import subprocess 9 | import argparse 10 | 11 | 12 | DEPS_DIR = ["../deps"] 13 | 14 | # Obtain the path to this script, plus a trailing separator. This will 15 | # be used later on to construct various environment variables for paths 16 | # to a variety of support directories. 17 | script_path = os.path.dirname(os.path.realpath(__file__)) + os.path.sep 18 | 19 | # Look through the specified file for known variables to get the dependency list 20 | def get_required_dependencies(filename): 21 | import ast 22 | 23 | # Always check the Python version 24 | dependencies = { 25 | 'python': 1 26 | } 27 | main_src = "" 28 | 29 | try: 30 | with open(sys.argv[0], 'r') as f: 31 | main_src = f.read() 32 | main_ast = ast.parse(main_src, filename=filename) 33 | except: 34 | return list(dependencies.keys()) 35 | 36 | # Iterate through the top-level nodes looking for variables named 37 | # LX_DEPENDENCIES or LX_DEPENDENCY and get the values that are 38 | # assigned to them. 39 | for node in ast.iter_child_nodes(main_ast): 40 | if isinstance(node, ast.Assign): 41 | value = node.value 42 | for target in node.targets: 43 | if isinstance(target, ast.Name): 44 | if target.id == "LX_DEPENDENCIES" or target.id == "LX_DEPENDENCY": 45 | if isinstance(value, (ast.List, ast.Tuple)): 46 | for elt in value.elts: 47 | if isinstance(elt, ast.Str): 48 | dependencies[elt.s] = 1 49 | elif isinstance(value, ast.Str): 50 | dependencies[value.s] = 1 51 | 52 | # Set up sub-dependencies 53 | if 'riscv' in dependencies: 54 | dependencies['make'] = 1 55 | return list(dependencies.keys()) 56 | 57 | def get_python_path(script_path, args, depdir): 58 | # Python has no concept of a local dependency path, such as the C `-I`` 59 | # switch, or the nodejs `node_modules` path, or the rust cargo registry. 60 | # Instead, it relies on an environment variable to append to the search 61 | # path. 62 | # Construct this variable by adding each subdirectory under the `deps/` 63 | # directory to the PYTHONPATH environment variable. 64 | python_path = [] 65 | for k in DEPS_DIR: 66 | if os.path.isdir(script_path + depdir): 67 | for dep in os.listdir(script_path + depdir): 68 | dep = script_path + k + os.path.sep + dep 69 | if os.path.isdir(dep): 70 | python_path.append(dep) 71 | return python_path 72 | 73 | def fixup_env(script_path, args): 74 | for k in DEPS_DIR: 75 | os.environ["PYTHONPATH"] = os.pathsep.join(get_python_path(script_path, 0, k)) 76 | 77 | # Set the "LXBUILDENV_REEXEC" variable to prevent the script from continuously 78 | # reinvoking itself. 79 | os.environ["LXBUILDENV_REEXEC"] = "1" 80 | 81 | # Python randomizes the order in which it traverses hashes, and Migen uses 82 | # hashes an awful lot when bringing together modules. As such, the order 83 | # in which Migen generates its output Verilog will change with every run, 84 | # and the addresses for various modules will change. 85 | # Make builds deterministic so that the generated Verilog code won't change 86 | # across runs. 87 | os.environ["PYTHONHASHSEED"] = "1" 88 | 89 | # Some Makefiles are invoked as part of the build process, and those Makefiles 90 | # occasionally have calls to Python. Ensure those Makefiles use the same 91 | # interpreter that this script is using. 92 | os.environ["PYTHON"] = sys.executable 93 | 94 | # Set the environment variable "V" to 1. This causes Makefiles to print 95 | # the commands they run, which makes them easier to debug. 96 | if args.lx_verbose: 97 | os.environ["V"] = "1" 98 | 99 | # If the user just wanted to print the environment variables, do that and quit. 100 | if args.lx_print_env: 101 | print("PYTHONPATH={}".format(os.environ["PYTHONPATH"])) 102 | print("PYTHONHASHSEED={}".format(os.environ["PYTHONHASHSEED"])) 103 | print("PYTHON={}".format(sys.executable)) 104 | print("LXBUILDENV_REEXEC={}".format(os.environ["LXBUILDENV_REEXEC"])) 105 | 106 | sys.exit(0) 107 | 108 | # Equivalent to the powershell Get-Command, and kinda like `which` 109 | def get_command(cmd): 110 | if os.name == 'nt': 111 | path_ext = os.environ["PATHEXT"].split(os.pathsep) 112 | else: 113 | path_ext = [""] 114 | for ext in path_ext: 115 | for path in os.environ["PATH"].split(os.pathsep): 116 | 117 | if os.path.exists(path + os.path.sep + cmd + ext): 118 | return path + os.path.sep + cmd + ext 119 | return None 120 | 121 | def check_python_version(args): 122 | import platform 123 | # Litex / Migen require Python 3.5 or newer. Ensure we're running 124 | # under a compatible version of Python. 125 | if sys.version_info[:3] < (3, 5): 126 | return (False, 127 | "python: You need Python 3.5+ (version {} found)".format(sys.version_info[:3])) 128 | return (True, "python 3.5+: ok (Python {} found)".format(platform.python_version())) 129 | 130 | def check_vivado(args): 131 | vivado_path = get_command("vivado") 132 | if vivado_path == None: 133 | # Look for the default Vivado install directory 134 | if os.name == 'nt': 135 | base_dir = r"C:\Xilinx\Vivado" 136 | else: 137 | base_dir = "/opt/Xilinx/Vivado" 138 | if os.path.exists(base_dir): 139 | for file in os.listdir(base_dir): 140 | bin_dir = base_dir + os.path.sep + file + os.path.sep + "bin" 141 | if os.path.exists(bin_dir + os.path.sep + "vivado"): 142 | os.environ["PATH"] += os.pathsep + bin_dir 143 | vivado_path = bin_dir 144 | break 145 | if vivado_path == None: 146 | return (False, "toolchain not found in your PATH", "download it from https://www.xilinx.com/support/download.html") 147 | return (True, "found at {}".format(vivado_path)) 148 | 149 | def check_cmd(args, cmd, name=None, fix=None): 150 | if name is None: 151 | name = cmd 152 | path = get_command(cmd) 153 | if path == None: 154 | return (False, name + " not found in your PATH", fix) 155 | return (True, "found at {}".format(path)) 156 | 157 | def check_make(args): 158 | return check_cmd(args, "make", "GNU Make") 159 | 160 | def check_riscv(args): 161 | return check_cmd(args, "riscv64-unknown-elf-gcc", "riscv toolchain", "download it from https://www.sifive.com/products/tools/") 162 | 163 | def check_yosys(args): 164 | return check_cmd(args, "yosys") 165 | 166 | def check_arachne(args): 167 | return check_cmd(args, "arachne-pnr") 168 | 169 | dependency_checkers = { 170 | 'python': check_python_version, 171 | 'vivado': check_vivado, 172 | 'make': check_make, 173 | 'riscv': check_riscv, 174 | 'yosys': check_yosys, 175 | 'arachne-pnr': check_arachne, 176 | } 177 | 178 | # Validate that the required dependencies (Vivado, compilers, etc.) 179 | # have been installed. 180 | def check_dependencies(args, dependency_list): 181 | 182 | dependency_errors = 0 183 | for dependency_name in dependency_list: 184 | if not dependency_name in dependency_checkers: 185 | print('WARNING: Unrecognized dependency "{}"'.format(dependency_name)) 186 | continue 187 | result = dependency_checkers[dependency_name](args) 188 | if result[0] == False: 189 | if len(result) > 2: 190 | print('{}: {} -- {}'.format(dependency_name, result[1], result[2])) 191 | else: 192 | print('{}: {}'.format(dependency_name, result[1])) 193 | dependency_errors = dependency_errors + 1 194 | 195 | elif args.lx_check_deps or args.lx_verbose: 196 | print('dependency: {}: {}'.format(dependency_name, result[1])) 197 | if dependency_errors > 0: 198 | if args.lx_ignore_deps: 199 | print('{} missing dependencies were found but continuing anyway'.format(dependency_errors)) 200 | else: 201 | raise SystemExit(str(dependency_errors) + 202 | " missing dependencies were found") 203 | 204 | if args.lx_check_deps: 205 | sys.exit(0) 206 | 207 | # Return True if the given tree needs to be initialized 208 | def check_module_recursive(root_path, depth, verbose=False): 209 | if verbose: 210 | print('git-dep: checking if "{}" requires updating...'.format(root_path)) 211 | # If the directory isn't a valid git repo, initialization is required 212 | if not os.path.exists(root_path + os.path.sep + '.git'): 213 | return True 214 | 215 | # If there are no submodules, no initialization needs to be done 216 | if not os.path.isfile(root_path + os.path.sep + '.gitmodules'): 217 | return False 218 | 219 | # Loop through the gitmodules to check all submodules 220 | gitmodules = open(root_path + os.path.sep + '.gitmodules', 'r') 221 | for line in gitmodules: 222 | parts = line.split("=", 2) 223 | if parts[0].strip() == "path": 224 | path = parts[1].strip() 225 | if check_module_recursive(root_path + os.path.sep + path, depth + 1, verbose=verbose): 226 | return True 227 | return False 228 | 229 | # Determine whether we need to invoke "git submodules init --recurse" 230 | def check_submodules(script_path, args): 231 | if check_module_recursive(script_path, 0, verbose=args.lx_verbose): 232 | print("Missing submodules -- updating") 233 | subprocess.Popen(["git", "submodule", "update", 234 | "--init", "--recursive"], cwd=script_path).wait() 235 | elif args.lx_verbose: 236 | print("Submodule check: Submodules found") 237 | 238 | 239 | def main(args): 240 | if args.init: 241 | main_name = os.getcwd().split(os.path.sep)[-1] + '.py' 242 | new_main_name = input('What would you like your main program to be called? [' + main_name + '] ') 243 | if new_main_name is not None and new_main_name != "": 244 | main_name = new_main_name 245 | 246 | print("Initializing git repository") 247 | if not os.path.exists(DEPS_DIR): 248 | os.mkdir(DEPS_DIR) 249 | 250 | os.system("git init") 251 | os.system("git add " + str(__file__)) 252 | 253 | os.system("git submodule add https://github.com/m-labs/migen.git deps/migen") 254 | os.system("git add deps/migen") 255 | 256 | os.system("git submodule add https://github.com/enjoy-digital/litex.git deps/litex") 257 | os.system("git add deps/litex") 258 | 259 | os.system("git submodule add https://github.com/enjoy-digital/litescope deps/litescope") 260 | os.system("git add deps/litescope") 261 | 262 | os.system("git submodule add https://github.com/pyserial/pyserial.git deps/pyserial") 263 | os.system("git add deps/pyserial") 264 | 265 | os.system("git submodule update --init --recursive") 266 | 267 | bin_tools = { 268 | 'litex_server': 'litex.soc.tools.remote.litex_server', 269 | 'litex_term': 'litex.soc.tools.litex_term', 270 | 'mkmscimg': 'litex.soc.tools.mkmscimg', 271 | } 272 | bin_template = """ 273 | #!/usr/bin/env python3 274 | 275 | import sys 276 | import os 277 | 278 | # This script lives in the "bin" directory, but uses a helper script in the parent 279 | # directory. Obtain the current path so we can get the absolute parent path. 280 | script_path = os.path.dirname(os.path.realpath( 281 | __file__)) + os.path.sep + os.path.pardir + os.path.sep 282 | sys.path.insert(0, script_path)it 283 | import lxbuildenv 284 | 285 | from litex.soc.tools.mkmscimg import main 286 | main()""" 287 | # Create binary programs under bin/ 288 | if not os.path.exists("bin"): 289 | print("Creating binaries") 290 | os.mkdir("bin") 291 | for bin_name, python_module in bin_tools.items(): 292 | with open('bin' + os.path.sep + bin_name, 'w') as new_bin: 293 | new_bin.write(bin_template) 294 | new_bin.write('from ' + python_module + ' import main\n') 295 | new_bin.write('main()\n') 296 | os.system('git add --chmod=+x bin' + os.path.sep + bin_name) 297 | 298 | with open(main_name, 'w') as m: 299 | program_template = """#!/usr/bin/env python3 300 | # This variable defines all the external programs that this module 301 | # relies on. lxbuildenv reads this variable in order to ensure 302 | # the build will finish without exiting due to missing third-party 303 | # programs. 304 | LX_DEPENDENCIES = ["riscv", "vivado"] 305 | 306 | # Import lxbuildenv to integrate the deps/ directory 307 | import lxbuildenv 308 | 309 | from migen import * 310 | from litex.build.generic_platform import * 311 | 312 | _io = [ 313 | ("clk50", 0, Pins("J19"), IOStandard("LVCMOS33")), 314 | ] 315 | 316 | class Platform(XilinxPlatform): 317 | def __init__(self, toolchain="vivado", programmer="vivado", part="35"): 318 | part = "xc7a" + part + "t-fgg484-2" 319 | def create_programmer(self): 320 | if self.programmer == "vivado": 321 | return VivadoProgrammer(flash_part="n25q128-3.3v-spi-x1_x2_x4") 322 | else: 323 | raise ValueError("{} programmer is not supported" 324 | .format(self.programmer)) 325 | 326 | def do_finalize(self, fragment): 327 | XilinxPlatform.do_finalize(self, fragment) 328 | 329 | class BaseSoC(SoCSDRAM): 330 | csr_peripherals = [ 331 | "ddrphy", 332 | # "dna", 333 | "xadc", 334 | "cpu_or_bridge", 335 | ] 336 | csr_map_update(SoCSDRAM.csr_map, csr_peripherals) 337 | 338 | def __init__(self, platform, **kwargs): 339 | clk_freq = int(100e6) 340 | 341 | def main(): 342 | platform = Platform() 343 | soc = BaseSoC(platform) 344 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 345 | vns = builder.build() 346 | soc.do_exit(vns) 347 | 348 | if __name__ == "__main__": 349 | main() 350 | """ 351 | m.write(program_template) 352 | return 353 | 354 | # For the main command, parse args and hand it off to main() 355 | if __name__ == "__main__": 356 | parser = argparse.ArgumentParser( 357 | description="Wrap Python code to enable quickstart", 358 | add_help=False) 359 | parser.add_argument( 360 | "-h", "--help", help="show this help message and exit", action="help" 361 | ) 362 | parser.add_argument( 363 | '-i', '--init', help='initialize a new project', action="store_true" 364 | ) 365 | args = parser.parse_args() 366 | 367 | main(args) 368 | 369 | elif not os.path.isfile(sys.argv[0]): 370 | print("lxbuildenv doesn't operate while in interactive mode") 371 | 372 | elif "LXBUILDENV_REEXEC" not in os.environ: 373 | parser = argparse.ArgumentParser( 374 | description="Wrap Python code to enable quickstart", 375 | add_help=False) 376 | parser.add_argument( 377 | "--lx-verbose", help="increase verboseness of some processes", action="store_true" 378 | ) 379 | parser.add_argument( 380 | "--lx-print-env", help="print environment variable listing for pycharm, vscode, or bash", action="store_true" 381 | ) 382 | parser.add_argument( 383 | "--lx-check-deps", help="check build environment for dependencies such as compiler and fpga tools and then exit", action="store_true" 384 | ) 385 | parser.add_argument( 386 | "--lx-all-deps", help="print all possible dependencies and then exit", action="store_true" 387 | ) 388 | parser.add_argument( 389 | "--lx-help", action="help" 390 | ) 391 | parser.add_argument( 392 | "--lx-ignore-deps", help="try building even if dependencies are missing", action="store_true" 393 | ) 394 | (args, rest) = parser.parse_known_args() 395 | 396 | if args.lx_all_deps: 397 | print('Known dependencies:') 398 | for dep in dependency_checkers.keys(): 399 | print(' {}'.format(dep)) 400 | print('To define a dependency, add a variable inside {} at the top level called LX_DEPENDENCIES and assign it a list or tuple.'.format(sys.argv[0])) 401 | print('For example:') 402 | print('LX_DEPENDENCIES = ("riscv", "vivado")') 403 | sys.exit(0) 404 | 405 | deps = get_required_dependencies(sys.argv[0]) 406 | 407 | fixup_env(script_path, args) 408 | check_dependencies(args, deps) 409 | check_submodules(script_path, args) 410 | 411 | try: 412 | sys.exit(subprocess.Popen( 413 | [sys.executable] + [sys.argv[0]] + rest).wait()) 414 | except: 415 | sys.exit(1) 416 | else: 417 | # Overwrite the deps directory. 418 | # Because we're running with a predefined PYTHONPATH, you'd think that 419 | # the DEPS_DIR would be first. 420 | # Unfortunately, setuptools causes the sitewide packages to take precedence 421 | # over the PYTHONPATH variable. 422 | # Work around this bug by inserting paths into the first index. 423 | for k in DEPS_DIR: 424 | for path in get_python_path(script_path, None, k): 425 | sys.path.insert(0, path) 426 | -------------------------------------------------------------------------------- /overlay/delay_controller.v: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2012-2015 Xilinx, Inc. 3 | // This design is confidential and proprietary of Xilinx, All Rights Reserved. 4 | ////////////////////////////////////////////////////////////////////////////// 5 | // ____ ____ 6 | // / /\/ / 7 | // /___/ \ / Vendor: Xilinx 8 | // \ \ \/ Version: 1.2 9 | // \ \ Filename: delay_controller_wrap.v 10 | // / / Date Last Modified: 21JAN2015 11 | // /___/ /\ Date Created: 8JAN2013 12 | // \ \ / \ 13 | // \___\/\___\ 14 | // 15 | //Device: 7 Series 16 | //Purpose: Controls delays on a per-bit basis 17 | // Number of bits from each seres set via an attribute 18 | // 19 | //Reference: XAPP585 20 | // 21 | //Revision History: 22 | // Rev 1.0 - First created (nicks) 23 | // Rev 1.2 - Updated format (brandond) 24 | // 25 | ////////////////////////////////////////////////////////////////////////////// 26 | // 27 | // Disclaimer: 28 | // 29 | // This disclaimer is not a license and does not grant any rights to the materials 30 | // distributed herewith. Except as otherwise provided in a valid license issued to you 31 | // by Xilinx, and to the maximum extent permitted by applicable law: 32 | // (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, 33 | // AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, 34 | // INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR 35 | // FITNESS FOR ANY PARTICULAR PURPOSE; and (2) Xilinx shall not be liable (whether in contract 36 | // or tort, including negligence, or under any other theory of liability) for any loss or damage 37 | // of any kind or nature related to, arising under or in connection with these materials, 38 | // including for any direct, or any indirect, special, incidental, or consequential loss 39 | // or damage (including loss of data, profits, goodwill, or any type of loss or damage suffered 40 | // as a result of any action brought by a third party) even if such damage or loss was 41 | // reasonably foreseeable or Xilinx had been advised of the possibility of the same. 42 | // 43 | // Critical Applications: 44 | // 45 | // Xilinx products are not designed or intended to be fail-safe, or for use in any application 46 | // requiring fail-safe performance, such as life-support or safety devices or systems, 47 | // Class III medical devices, nuclear facilities, applications related to the deployment of airbags, 48 | // or any other applications that could lead to death, personal injury, or severe property or 49 | // environmental damage (individually and collectively, "Critical Applications"). Customer assumes 50 | // the sole risk and liability of any use of Xilinx products in Critical Applications, subject only 51 | // to applicable laws and regulations governing limitations on product liability. 52 | // 53 | // THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES. 54 | // 55 | ////////////////////////////////////////////////////////////////////////////// 56 | 57 | `timescale 1ps/1ps 58 | 59 | module delay_controller (m_datain, s_datain, enable_phase_detector, enable_monitor, reset, clk, c_delay_in, m_delay_out, s_delay_out, data_out, bt_val, results, m_delay_1hot, del_mech) ; 60 | 61 | parameter integer S = 8 ; // Set the number of bits 62 | 63 | input [S-1:0] m_datain ; // Inputs from master serdes 64 | input [S-1:0] s_datain ; // Inputs from slave serdes 65 | input enable_phase_detector ; // Enables the phase detector logic when high 66 | input enable_monitor ; // Enables the eye monitoring logic when high 67 | input reset ; // Reset line synchronous to clk 68 | input clk ; // Global/Regional clock 69 | input [4:0] c_delay_in ; // delay value found on clock line 70 | output [4:0] m_delay_out ; // Master delay control value 71 | output [4:0] s_delay_out ; // Master delay control value 72 | output reg [S-1:0] data_out ; // Output data 73 | input [4:0] bt_val ; // Calculated bit time value for slave devices 74 | output reg [31:0] results ; // eye monitor result data 75 | output reg [31:0] m_delay_1hot ; // Master delay control value as a one-hot vector 76 | input del_mech ; // changes delay mechanism slightly at higher bit rates 77 | 78 | reg [S-1:0] mdataouta ; 79 | reg mdataoutb ; 80 | reg [S-1:0] mdataoutc ; 81 | reg [S-1:0] sdataouta ; 82 | reg sdataoutb ; 83 | reg [S-1:0] sdataoutc ; 84 | reg s_ovflw ; 85 | reg [1:0] m_delay_mux ; 86 | reg [1:0] s_delay_mux ; 87 | reg data_mux ; 88 | reg dec_run ; 89 | reg inc_run ; 90 | reg eye_run ; 91 | reg [4:0] s_state ; 92 | reg [5:0] pdcount ; 93 | reg [4:0] m_delay_val_int ; 94 | reg [4:0] s_delay_val_int ; 95 | reg [4:0] s_delay_val_eye ; 96 | reg meq_max ; 97 | reg meq_min ; 98 | reg pd_max ; 99 | reg pd_min ; 100 | reg delay_change ; 101 | wire [S-1:0] all_high ; 102 | wire [S-1:0] all_low ; 103 | wire [7:0] msxoria ; 104 | wire [7:0] msxorda ; 105 | reg [1:0] action ; 106 | reg [1:0] msxor_cti ; 107 | reg [1:0] msxor_ctd ; 108 | reg [1:0] msxor_ctix ; 109 | reg [1:0] msxor_ctdx ; 110 | wire [2:0] msxor_ctiy ; 111 | wire [2:0] msxor_ctdy ; 112 | reg [7:0] match ; 113 | reg [31:0] shifter ; 114 | reg [7:0] pd_hold ; 115 | 116 | assign m_delay_out = m_delay_val_int ; 117 | assign s_delay_out = s_delay_val_int ; 118 | genvar i ; 119 | 120 | generate 121 | 122 | for (i = 0 ; i <= S-2 ; i = i+1) begin : loop0 123 | 124 | assign msxoria[i+1] = ((~s_ovflw & ((mdataouta[i] & ~mdataouta[i+1] & ~sdataouta[i]) | (~mdataouta[i] & mdataouta[i+1] & sdataouta[i]))) | 125 | ( s_ovflw & ((mdataouta[i] & ~mdataouta[i+1] & ~sdataouta[i+1]) | (~mdataouta[i] & mdataouta[i+1] & sdataouta[i+1])))) ; // early bits 126 | assign msxorda[i+1] = ((~s_ovflw & ((mdataouta[i] & ~mdataouta[i+1] & sdataouta[i]) | (~mdataouta[i] & mdataouta[i+1] & ~sdataouta[i])))) | 127 | ( s_ovflw & ((mdataouta[i] & ~mdataouta[i+1] & sdataouta[i+1]) | (~mdataouta[i] & mdataouta[i+1] & ~sdataouta[i+1]))) ; // late bits 128 | end 129 | endgenerate 130 | 131 | assign msxoria[0] = ((~s_ovflw & ((mdataoutb & ~mdataouta[0] & ~sdataoutb) | (~mdataoutb & mdataouta[0] & sdataoutb))) | // first early bit 132 | ( s_ovflw & ((mdataoutb & ~mdataouta[0] & ~sdataouta[0]) | (~mdataoutb & mdataouta[0] & sdataouta[0])))) ; 133 | assign msxorda[0] = ((~s_ovflw & ((mdataoutb & ~mdataouta[0] & sdataoutb) | (~mdataoutb & mdataouta[0] & ~sdataoutb)))) | // first late bit 134 | ( s_ovflw & ((mdataoutb & ~mdataouta[0] & sdataouta[0]) | (~mdataoutb & mdataouta[0] & ~sdataouta[0]))) ; 135 | 136 | always @ (posedge clk) begin // generate number of incs or decs for low 4 bits 137 | case (msxoria[3:0]) // m != s bit count 138 | 4'h0 : msxor_cti <= 2'h0 ; 139 | 4'h1 : msxor_cti <= 2'h1 ; 140 | 4'h2 : msxor_cti <= 2'h1 ; 141 | 4'h3 : msxor_cti <= 2'h2 ; 142 | 4'h4 : msxor_cti <= 2'h1 ; 143 | 4'h5 : msxor_cti <= 2'h2 ; 144 | 4'h6 : msxor_cti <= 2'h2 ; 145 | 4'h8 : msxor_cti <= 2'h1 ; 146 | 4'h9 : msxor_cti <= 2'h2 ; 147 | 4'hA : msxor_cti <= 2'h2 ; 148 | 4'hC : msxor_cti <= 2'h2 ; 149 | default : msxor_cti <= 2'h3 ; 150 | endcase 151 | case (msxorda[3:0]) // m == s bit count 152 | 4'h0 : msxor_ctd <= 2'h0 ; 153 | 4'h1 : msxor_ctd <= 2'h1 ; 154 | 4'h2 : msxor_ctd <= 2'h1 ; 155 | 4'h3 : msxor_ctd <= 2'h2 ; 156 | 4'h4 : msxor_ctd <= 2'h1 ; 157 | 4'h5 : msxor_ctd <= 2'h2 ; 158 | 4'h6 : msxor_ctd <= 2'h2 ; 159 | 4'h8 : msxor_ctd <= 2'h1 ; 160 | 4'h9 : msxor_ctd <= 2'h2 ; 161 | 4'hA : msxor_ctd <= 2'h2 ; 162 | 4'hC : msxor_ctd <= 2'h2 ; 163 | default : msxor_ctd <= 2'h3 ; 164 | endcase 165 | case (msxoria[7:4]) // generate number of incs or decs for high n bits, max 4 166 | 4'h0 : msxor_ctix <= 2'h0 ; 167 | 4'h1 : msxor_ctix <= 2'h1 ; 168 | 4'h2 : msxor_ctix <= 2'h1 ; 169 | 4'h3 : msxor_ctix <= 2'h2 ; 170 | 4'h4 : msxor_ctix <= 2'h1 ; 171 | 4'h5 : msxor_ctix <= 2'h2 ; 172 | 4'h6 : msxor_ctix <= 2'h2 ; 173 | 4'h8 : msxor_ctix <= 2'h1 ; 174 | 4'h9 : msxor_ctix <= 2'h2 ; 175 | 4'hA : msxor_ctix <= 2'h2 ; 176 | 4'hC : msxor_ctix <= 2'h2 ; 177 | default : msxor_ctix <= 2'h3 ; 178 | endcase 179 | case (msxorda[7:4]) 180 | 4'h0 : msxor_ctdx <= 2'h0 ; 181 | 4'h1 : msxor_ctdx <= 2'h1 ; 182 | 4'h2 : msxor_ctdx <= 2'h1 ; 183 | 4'h3 : msxor_ctdx <= 2'h2 ; 184 | 4'h4 : msxor_ctdx <= 2'h1 ; 185 | 4'h5 : msxor_ctdx <= 2'h2 ; 186 | 4'h6 : msxor_ctdx <= 2'h2 ; 187 | 4'h8 : msxor_ctdx <= 2'h1 ; 188 | 4'h9 : msxor_ctdx <= 2'h2 ; 189 | 4'hA : msxor_ctdx <= 2'h2 ; 190 | 4'hC : msxor_ctdx <= 2'h2 ; 191 | default : msxor_ctdx <= 2'h3 ; 192 | endcase 193 | end 194 | 195 | assign msxor_ctiy = {1'b0, msxor_cti} + {1'b0, msxor_ctix} ; // m != s bit count 196 | assign msxor_ctdy = {1'b0, msxor_ctd} + {1'b0, msxor_ctdx} ; // m == s bit count 197 | 198 | always @ (posedge clk) begin 199 | if (msxor_ctiy == msxor_ctdy) begin 200 | action <= 2'h0 ; 201 | end 202 | else if (msxor_ctiy > msxor_ctdy) begin // m!=s bits > m==s bits 203 | action <= 2'h1 ; // m/s bits differ 204 | end 205 | else begin 206 | action <= 2'h2 ; // m/s bits same 207 | end 208 | end 209 | 210 | generate 211 | for (i = 0 ; i <= S-1 ; i = i+1) begin : loop1 212 | assign all_high[i] = 1'b1 ; 213 | assign all_low[i] = 1'b0 ; 214 | end 215 | endgenerate 216 | 217 | always @ (posedge clk) begin 218 | mdataouta <= m_datain ; 219 | mdataoutb <= mdataouta[S-1] ; 220 | sdataouta <= s_datain ; 221 | sdataoutb <= sdataouta[S-1] ; 222 | end 223 | 224 | always @ (posedge clk) begin 225 | if (reset == 1'b1) begin 226 | s_ovflw <= 1'b0 ; 227 | pdcount <= 6'b100000 ; 228 | m_delay_val_int <= c_delay_in ; // initial master delay 229 | s_delay_val_int <= c_delay_in ; // initial slave delay 230 | data_mux <= 1'b0 ; 231 | m_delay_mux <= 2'b01 ; 232 | s_delay_mux <= 2'b01 ; 233 | s_state <= 5'b00000 ; 234 | inc_run <= 1'b0 ; 235 | dec_run <= 1'b0 ; 236 | eye_run <= 1'b0 ; 237 | s_delay_val_eye <= 5'h00 ; 238 | shifter <= 32'h00000001 ; 239 | delay_change <= 1'b0 ; 240 | results <= 32'h00000000 ; 241 | pd_hold <= 8'h00 ; 242 | end 243 | else begin 244 | case (m_delay_mux) 245 | 2'b00 : mdataoutc <= {mdataouta[S-2:0], mdataoutb} ; 246 | 2'b10 : mdataoutc <= {m_datain[0], mdataouta[S-1:1]} ; 247 | default : mdataoutc <= mdataouta ; 248 | endcase 249 | case (s_delay_mux) 250 | 2'b00 : sdataoutc <= {sdataouta[S-2:0], sdataoutb} ; 251 | 2'b10 : sdataoutc <= {s_datain[0], sdataouta[S-1:1]} ; 252 | default : sdataoutc <= sdataouta ; 253 | endcase // case (s_delay_mux) 254 | 255 | if (m_delay_val_int == bt_val) begin 256 | meq_max <= 1'b1 ; 257 | end else begin 258 | meq_max <= 1'b0 ; 259 | end 260 | 261 | if (m_delay_val_int == 5'h00) begin 262 | meq_min <= 1'b1 ; 263 | end else begin 264 | meq_min <= 1'b0 ; 265 | end 266 | 267 | if (pdcount == 6'h3F && pd_max == 1'b0 && delay_change == 1'b0) begin 268 | pd_max <= 1'b1 ; 269 | end else begin 270 | pd_max <= 1'b0 ; 271 | end 272 | 273 | if (pdcount == 6'h00 && pd_min == 1'b0 && delay_change == 1'b0) begin 274 | pd_min <= 1'b1 ; 275 | end else begin 276 | pd_min <= 1'b0 ; 277 | end 278 | 279 | 280 | // aim to have slave sampler dither about the transition point, forcing master to be in the middle of the eye 281 | if (delay_change == 1'b1 || inc_run == 1'b1 || dec_run == 1'b1 || eye_run == 1'b1) begin 282 | pd_hold <= 8'hFF ; 283 | pdcount <= 6'b100000 ; 284 | end // increment filter count 285 | else if (pd_hold[7] == 1'b1) begin 286 | pdcount <= 6'b100000 ; 287 | pd_hold <= {pd_hold[6:0], 1'b0} ; 288 | end 289 | else if (action[0] == 1'b1 && pdcount != 6'b111111) begin 290 | pdcount <= pdcount + 6'h01 ; // action = 01 delay was too short; increment delay value, pushing slave away from transition (master/slave bits differ) 291 | end // decrement filter count 292 | else if (action[1] == 1'b1 && pdcount != 6'b000000) begin 293 | pdcount <= pdcount - 6'h01 ; // action = 10 delay was too long; decrement delay value, pushing slave toward transition (master/slave bits same) 294 | end 295 | 296 | 297 | if ((enable_phase_detector == 1'b1 && pd_max == 1'b1 && delay_change == 1'b0) || inc_run == 1'b1) begin // increment delays, check for master delay = max 298 | delay_change <= 1'b1 ; 299 | if (meq_max == 1'b0 && inc_run == 1'b0) begin 300 | m_delay_val_int <= m_delay_val_int + 5'h01 ; 301 | end 302 | else begin // master is max 303 | s_state[3:0] <= s_state[3:0] + 4'h1 ; 304 | case (s_state[3:0]) 305 | 4'b0000 : begin inc_run <= 1'b1 ; s_delay_val_int <= bt_val ; end // indicate state machine running and set slave delay to bit time 306 | 4'b0110 : begin data_mux <= 1'b1 ; m_delay_val_int <= 5'b00000 ; end // change data mux over to forward slave data and set master delay to zero 307 | 4'b1001 : begin m_delay_mux <= m_delay_mux - 2'h1 ; end // change delay mux over to forward with a 1-bit less advance 308 | 4'b1110 : begin data_mux <= 1'b0 ; end // change data mux over to forward master data 309 | 4'b1111 : begin s_delay_mux <= m_delay_mux ; inc_run <= 1'b0 ; end // change delay mux over to forward with a 1-bit less advance 310 | default : begin inc_run <= 1'b1 ; end 311 | endcase 312 | end 313 | end 314 | else if ((enable_phase_detector == 1'b1 && pd_min == 1'b1 && delay_change == 1'b0) || dec_run == 1'b1) begin // decrement delays, check for master delay = 0 315 | delay_change <= 1'b1 ; 316 | if (meq_min == 1'b0 && dec_run == 1'b0) begin 317 | m_delay_val_int <= m_delay_val_int - 5'h01 ; 318 | end 319 | else begin // master is zero 320 | s_state[3:0] <= s_state[3:0] + 4'h1 ; 321 | case (s_state[3:0]) 322 | 4'b0000 : begin dec_run <= 1'b1 ; s_delay_val_int <= 5'b00000 ; end // indicate state machine running and set slave delay to zero 323 | 4'b0110 : begin data_mux <= 1'b1 ; m_delay_val_int <= bt_val ; end // change data mux over to forward slave data and set master delay to bit time 324 | 4'b1001 : begin m_delay_mux <= m_delay_mux + 2'h1 ; end // change delay mux over to forward with a 1-bit more advance 325 | 4'b1110 : begin data_mux <= 1'b0 ; end // change data mux over to forward master data 326 | 4'b1111 : begin s_delay_mux <= m_delay_mux ; dec_run <= 1'b0 ; end // change delay mux over to forward with a 1-bit less advance 327 | default : begin dec_run <= 1'b1 ; end 328 | endcase 329 | end 330 | end 331 | else if (enable_monitor == 1'b1 && (eye_run == 1'b1 || delay_change == 1'b1)) begin 332 | delay_change <= 1'b0 ; 333 | s_state <= s_state + 5'h01 ; 334 | case (s_state) 335 | 5'b00000 : begin eye_run <= 1'b1 ; s_delay_val_int <= s_delay_val_eye ; end // indicate state machine running and set slave delay to monitor value 336 | 5'b10110 : begin 337 | if (match == 8'hFF) begin results <= results | shifter ; end //. set or clear result bit 338 | else begin results <= results & ~shifter ; end 339 | if (s_delay_val_eye == bt_val) begin // only monitor active taps, ie as far as btval 340 | shifter <= 32'h00000001 ; s_delay_val_eye <= 5'h00 ; end 341 | else begin shifter <= {shifter[30:0], shifter[31]} ; 342 | s_delay_val_eye <= s_delay_val_eye + 5'h01 ; end // 343 | eye_run <= 1'b0 ; s_state <= 5'h00 ; end 344 | default : begin eye_run <= 1'b1 ; end 345 | endcase 346 | end 347 | else begin 348 | delay_change <= 1'b0 ; 349 | if (m_delay_val_int >= {1'b0, bt_val[4:1]} && del_mech == 1'b0) begin // set slave delay to 1/2 bit period beyond or behind the master delay 350 | s_delay_val_int <= m_delay_val_int - {1'b0, bt_val[4:1]} ; 351 | s_ovflw <= 1'b0 ; 352 | end 353 | else begin 354 | s_delay_val_int <= m_delay_val_int + {1'b0, bt_val[4:1]} ; 355 | s_ovflw <= 1'b1 ; 356 | end 357 | end // else: !if(enable_monitor == 1'b1 && (eye_run == 1'b1 || delay_change == 1'b1)) 358 | 359 | if (enable_phase_detector == 1'b0 && delay_change == 1'b0) begin 360 | delay_change <= 1'b1 ; 361 | end 362 | 363 | end // else: !if(reset == 1'b1) 364 | 365 | if (enable_phase_detector == 1'b1) begin 366 | if (data_mux == 1'b0) begin 367 | data_out <= mdataoutc ; 368 | end else begin 369 | data_out <= sdataoutc ; 370 | end 371 | end 372 | else begin 373 | data_out <= m_datain ; 374 | end 375 | end 376 | 377 | always @ (posedge clk) begin 378 | if ((mdataouta == sdataouta)) begin 379 | match <= {match[6:0], 1'b1} ; 380 | end else begin 381 | match <= {match[6:0], 1'b0} ; 382 | end 383 | end 384 | 385 | always @ (m_delay_val_int) begin 386 | case (m_delay_val_int) 387 | 5'b00000 : m_delay_1hot <= 32'h00000001 ; 388 | 5'b00001 : m_delay_1hot <= 32'h00000002 ; // red 389 | 5'b00010 : m_delay_1hot <= 32'h00000004 ; // red 390 | 5'b00011 : m_delay_1hot <= 32'h00000008 ; 391 | 5'b00100 : m_delay_1hot <= 32'h00000010 ; 392 | 5'b00101 : m_delay_1hot <= 32'h00000020 ; // others 393 | 5'b00110 : m_delay_1hot <= 32'h00000040 ; // others 394 | 5'b00111 : m_delay_1hot <= 32'h00000080 ; 395 | 5'b01000 : m_delay_1hot <= 32'h00000100 ; 396 | 5'b01001 : m_delay_1hot <= 32'h00000200 ; 397 | 5'b01010 : m_delay_1hot <= 32'h00000400 ; 398 | 5'b01011 : m_delay_1hot <= 32'h00000800 ; 399 | 5'b01100 : m_delay_1hot <= 32'h00001000 ; 400 | 5'b01101 : m_delay_1hot <= 32'h00002000 ; 401 | 5'b01110 : m_delay_1hot <= 32'h00004000 ; 402 | 5'b01111 : m_delay_1hot <= 32'h00008000 ; 403 | 5'b10000 : m_delay_1hot <= 32'h00010000 ; 404 | 5'b10001 : m_delay_1hot <= 32'h00020000 ; 405 | 5'b10010 : m_delay_1hot <= 32'h00040000 ; 406 | 5'b10011 : m_delay_1hot <= 32'h00080000 ; 407 | 5'b10100 : m_delay_1hot <= 32'h00100000 ; 408 | 5'b10101 : m_delay_1hot <= 32'h00200000 ; 409 | 5'b10110 : m_delay_1hot <= 32'h00400000 ; 410 | 5'b10111 : m_delay_1hot <= 32'h00800000 ; 411 | 5'b11000 : m_delay_1hot <= 32'h01000000 ; 412 | 5'b11001 : m_delay_1hot <= 32'h02000000 ; 413 | 5'b11010 : m_delay_1hot <= 32'h04000000 ; 414 | 5'b11011 : m_delay_1hot <= 32'h08000000 ; 415 | 5'b11100 : m_delay_1hot <= 32'h10000000 ; 416 | 5'b11101 : m_delay_1hot <= 32'h20000000 ; 417 | 5'b11110 : m_delay_1hot <= 32'h40000000 ; 418 | default : m_delay_1hot <= 32'h80000000 ; 419 | endcase 420 | end 421 | 422 | endmodule 423 | --------------------------------------------------------------------------------