├── doc ├── netv2_schematic.pdf └── netv2mvp_schematic.pdf ├── firmware ├── bist.h ├── ci.h ├── km.h ├── hdmi_out0.h ├── stdio_wrap.h ├── dump.h ├── asm.h ├── config.h ├── isr.c ├── stdio_wrap.c ├── config.c ├── hdmi_in0.h ├── hdmi_in1.h ├── i2c.h ├── edid.h ├── pattern.h ├── processor.h ├── linker.ld ├── flags.h ├── encoder.h ├── mmcm.h ├── Makefile ├── main.c ├── bist.c ├── hdmi_out0.c ├── i2c.c ├── dump.c ├── km.c ├── pattern.c ├── encoder.c ├── edid.c ├── hdmi_in0.c └── hdmi_in1.c ├── litescope ├── analyzer.csv ├── test_io.py ├── test_analyzer.py~ ├── test_analyzer.py ├── csr.csv ├── build.py~ └── build.py ├── .gitignore ├── load.py ├── 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 ├── TODO ├── test └── test_identifier.py ├── bin ├── mkmscimg ├── litex_term └── litex_server ├── .gitmodules ├── program-fpga.sh ├── overlay ├── diff_network.v ├── shuffle_network.v ├── hdcp_lfsr.v ├── hdcp_mod.v └── hdcp_cipher.v ├── gateware └── dma.py ├── README.adoc ├── dma_test.py ├── netv2.py └── lxbuildenv.py /doc/netv2_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xobs/netv2-fpga/HEAD/doc/netv2_schematic.pdf -------------------------------------------------------------------------------- /doc/netv2mvp_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xobs/netv2-fpga/HEAD/doc/netv2mvp_schematic.pdf -------------------------------------------------------------------------------- /firmware/bist.h: -------------------------------------------------------------------------------- 1 | #ifndef __BIST_H 2 | #define __BIST_H 3 | 4 | void bist_test(void); 5 | 6 | #endif /* __BIST_H */ 7 | -------------------------------------------------------------------------------- /litescope/analyzer.csv: -------------------------------------------------------------------------------- 1 | config,None,dw,128 2 | config,None,depth,512 3 | config,None,cd_ratio,1 4 | signal,0,bus,128 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /firmware/km.h: -------------------------------------------------------------------------------- 1 | void compute_keys( unsigned long Ksv_hi, unsigned long Ksv_lo, unsigned int source, unsigned long long *key ); 2 | void derive_km(void); 3 | 4 | -------------------------------------------------------------------------------- /load.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from litex.build.xilinx import VivadoProgrammer 3 | 4 | prog = VivadoProgrammer() 5 | prog.load_bitstream("build/gateware/top.bit") 6 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Add real hdmi support to litevideo input. (at least ignore specific hdmi data) 2 | - Inject captured hdmi data into litevideo input, simulate. 3 | - Test real hdmi support on board. 4 | - Add second HDMI in/out ports to NeTV2MVP. 5 | - Test Host Memory <--> DDR3 DMAs 6 | - Test Host Memory --> DDR3 --> VideoOut 7 | - Test VideoIn --> DDR3 --> Host Memory 8 | - Test PCIe Gen2 X4 (requires LitePCIe changes). -------------------------------------------------------------------------------- /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 { 12, 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 | -------------------------------------------------------------------------------- /litescope/test_io.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from litex.soc.tools.remote import RemoteClient 4 | 5 | from litescope import LiteScopeIODriver 6 | 7 | 8 | def led_anim(inout): 9 | import time 10 | for i in range(10): 11 | io.write(0xa5) 12 | time.sleep(0.1) 13 | io.write(0x5a) 14 | time.sleep(0.1) 15 | 16 | 17 | wb = RemoteClient() 18 | wb.open() 19 | 20 | # # # 21 | 22 | io = LiteScopeIODriver(wb.regs, "io") 23 | led_anim(io) 24 | print("{:04x}".format(io.read())) 25 | 26 | # # # 27 | 28 | wb.close() 29 | -------------------------------------------------------------------------------- /firmware/isr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hdmi_in0.h" 6 | #include "hdmi_in1.h" 7 | void isr(void); 8 | void isr(void) 9 | { 10 | unsigned int irqs; 11 | 12 | irqs = irq_pending() & irq_getmask(); 13 | 14 | if(irqs & (1 << UART_INTERRUPT)) 15 | uart_isr(); 16 | 17 | #ifdef CSR_HDMI_IN0_INTERRUPT 18 | if(irqs & (1 << HDMI_IN0_INTERRUPT)) 19 | hdmi_in0_isr(); 20 | #endif 21 | #ifdef HDMI_IN1_INTERRUPT 22 | if(irqs & (1 << HDMI_IN1_INTERRUPT)) { 23 | hdmi_in1_isr(); 24 | } 25 | #endif 26 | } 27 | -------------------------------------------------------------------------------- /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_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 | 7 | unsigned int hdmi_in0_framebuffer_base(char n); 8 | 9 | void hdmi_in0_isr(void); 10 | void hdmi_in0_init_video(int hres, int vres); 11 | void hdmi_in0_disable(void); 12 | void hdmi_in0_clear_framebuffers(void); 13 | void hdmi_in0_print_status(void); 14 | int hdmi_in0_calibrate_delays(int freq); 15 | int hdmi_in0_adjust_phase(void); 16 | int hdmi_in0_init_phase(void); 17 | int hdmi_in0_phase_startup(int freq); 18 | void hdmi_in0_service(int freq); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /litescope/test_analyzer.py~: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | 4 | from litex.soc.tools.remote import RemoteClient 5 | from litescope.software.driver.analyzer import LiteScopeAnalyzerDriver 6 | 7 | wb = RemoteClient() 8 | wb.open() 9 | 10 | # # # 11 | 12 | analyzer = LiteScopeAnalyzerDriver(wb.regs, "analyzer", debug=True) 13 | #analyzer.configure_trigger(cond={"charsync0_data": 0x354}) 14 | analyzer.configure_trigger(cond={"hdmi_in0_frame_de" : 1}) 15 | 16 | analyzer.configure_subsampler(1) 17 | analyzer.run(offset=32, length=256) 18 | analyzer.wait_done() 19 | analyzer.upload() 20 | analyzer.save("dump.vcd") 21 | 22 | # # # 23 | 24 | wb.close() 25 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /litescope/test_analyzer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import time 3 | import os 4 | import sys 5 | 6 | from litex.gen.fhdl.structure import * 7 | 8 | from litex.soc.tools.remote import RemoteClient 9 | from litescope.software.driver.analyzer import LiteScopeAnalyzerDriver 10 | 11 | wb = RemoteClient() 12 | wb.open() 13 | 14 | # # # 15 | 16 | analyzer = LiteScopeAnalyzerDriver(wb.regs, "analyzer", debug=True) 17 | #analyzer.configure_trigger(cond={"charsync0_data": 0x354}) 18 | #analyzer.configure_trigger(cond={"hdmi_in0_frame_de" : 1}) 19 | #analyzer.configure_trigger(cond={}) 20 | t = getattr(analyzer, "frontend_trigger_value") 21 | m = getattr(analyzer, "frontend_trigger_mask") 22 | t.write(0x80000000000000000) 23 | m.write(0x80000000000000000) 24 | 25 | analyzer.configure_subsampler(1) 26 | analyzer.run(offset=32, length=512) 27 | analyzer.wait_done() 28 | analyzer.upload() 29 | analyzer.save("dump.vcd") 30 | 31 | # # # 32 | 33 | wb.close() 34 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/litex"] 2 | path = deps/litex 3 | url = https://github.com/enjoy-digital/litex.git 4 | branch = master 5 | [submodule "deps/migen"] 6 | path = deps/migen 7 | url = https://github.com/bunnie/migen.git 8 | branch = improve-gearbox-timing 9 | [submodule "deps/litedram"] 10 | path = deps/litedram 11 | url = https://github.com/enjoy-digital/litedram.git 12 | [submodule "deps/litevideo"] 13 | path = deps/litevideo 14 | url = https://github.com/bunnie/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/bunnie/litescope.git 22 | branch = add-trigger-depth 23 | [submodule "deps/pyserial"] 24 | path = deps/pyserial 25 | url = https://github.com/pyserial/pyserial.git 26 | [submodule "deps/litepcie"] 27 | path = deps/litepcie 28 | url = https://github.com/enjoy-digital/litepcie.git 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /firmware/processor.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROCESSOR_H 2 | #define __PROCESSOR_H 3 | 4 | #define PROCESSOR_MODE_COUNT 14 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 | #endif /* __PROCESSOR_H */ 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 -------------------------------------------------------------------------------- /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 | 35 | #endif 36 | -------------------------------------------------------------------------------- /litescope/csr.csv: -------------------------------------------------------------------------------- 1 | csr_base,analyzer,0xe0008000,, 2 | csr_base,io,0xe0008800,, 3 | csr_register,analyzer_mux_value,0xe0008000,1,rw 4 | csr_register,analyzer_frontend_trigger_value,0xe0008004,4,rw 5 | csr_register,analyzer_frontend_trigger_mask,0xe0008014,4,rw 6 | csr_register,analyzer_frontend_subsampler_value,0xe0008024,1,rw 7 | csr_register,analyzer_storage_start,0xe0008028,1,rw 8 | csr_register,analyzer_storage_length,0xe000802c,1,rw 9 | csr_register,analyzer_storage_offset,0xe0008030,1,rw 10 | csr_register,analyzer_storage_idle,0xe0008034,1,ro 11 | csr_register,analyzer_storage_wait,0xe0008038,1,ro 12 | csr_register,analyzer_storage_run,0xe000803c,1,ro 13 | csr_register,analyzer_storage_mem_flush,0xe0008040,1,rw 14 | csr_register,analyzer_storage_mem_valid,0xe0008044,1,ro 15 | csr_register,analyzer_storage_mem_ready,0xe0008048,1,rw 16 | csr_register,analyzer_storage_mem_data,0xe000804c,4,ro 17 | csr_register,io_in,0xe0008800,1,ro 18 | csr_register,io_out,0xe0008804,1,rw 19 | constant,nmi_interrupt,0,, 20 | constant,uart_interrupt,2,, 21 | constant,csr_data_width,32,, 22 | constant,system_clock_frequency,100000000,, 23 | constant,config_clock_frequency,100000000,, 24 | constant,config_cpu_reset_addr,0,, 25 | constant,config_cpu_type,NONE,, 26 | constant,config_csr_data_width,32,, 27 | memory_region,sram,0x10000000,4096, 28 | -------------------------------------------------------------------------------- /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 | 31 | CFLAGS += -I. 32 | 33 | all: firmware.bin 34 | 35 | # pull in dependency info for *existing* .o files 36 | -include $(OBJECTS:.o=.d) 37 | 38 | %.bin: %.elf 39 | $(OBJCOPY) -O binary $< $@ 40 | ifneq ($(OS),Windows_NT) 41 | chmod -x $@ 42 | endif 43 | $(COPY) $@ boot.bin 44 | 45 | firmware.elf: $(OBJECTS) 46 | $(LD) $(LDFLAGS) \ 47 | -T linker.ld \ 48 | -N -o $@ \ 49 | $(NETV2_DIR)/software/libbase/crt0-$(CPU).o \ 50 | $(OBJECTS) \ 51 | -L$(NETV2_DIR)/software/libbase \ 52 | -L$(NETV2_DIR)/software/libcompiler_rt \ 53 | -lbase-nofloat -lcompiler_rt 54 | ifneq ($(OS),Windows_NT) 55 | chmod -x $@ 56 | endif 57 | 58 | main.o: main.c 59 | $(compile) 60 | 61 | %.o: %.c 62 | $(compile) 63 | 64 | %.o: %.S 65 | $(assemble) 66 | 67 | load: firmware.bin 68 | litex_term --kernel firmware.bin COM8 69 | 70 | clean: 71 | $(RM) $(OBJECTS) $(OBJECTS:.o=.d) firmware.elf firmware.bin .*~ *~ 72 | 73 | .PHONY: all main.o clean load 74 | -------------------------------------------------------------------------------- /program-fpga.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | OPENOCD=/opt/openocd-netv2mvp/bin/openocd 3 | 4 | if [ -z $1 ] 5 | then 6 | echo "Usage: $0 [bitstream]" 7 | exit 1 8 | fi 9 | 10 | if [ ! -e $1 ] 11 | then 12 | echo "Could not find bitstream file $1" 13 | exit 2 14 | fi 15 | 16 | # Raspi2 and Raspi3 peripheral_base address 17 | gpio_peripheral_base_addr=0x3F000000 18 | 19 | # Raspi1 peripheral_base address 20 | # gpio_peripheral_base=0x20000000 21 | 22 | 23 | # Speed coefficients 24 | 25 | # Raspi3 BCM2837 (1200Mhz): 26 | #gpio_speed_coeffs 194938 48 27 | 28 | # Raspi3 B (oscope tuned) 29 | #gpio_speed_coeffs 315000 24 30 | 31 | # Raspip 3B+ (oscope tuned) 32 | gpio_speed_coeffs="340000 10" 33 | 34 | # Raspi2 BCM2836 (900Mhz): 35 | # gpio_speed_coeffs 146203 36 36 | 37 | # Raspi1 BCM2835: (700Mhz) 38 | # gpio_speed_coeffs 113714 28 39 | 40 | 41 | # verified with oscope to be about as fast as you want it to go 42 | # on a 3B: 43 | # TCK duty cycle is asymmetric, unstable at 10MHz 44 | # starts to get marginal @ 6MHz, but solid at 5MHz 45 | # on a 3B+: 46 | # TCK duty cycle is solid at 6000MHz, using the correct speed coeffs above 47 | # 48 | # also, OpenOCD needs this patch: 49 | # pads_base[BCM2835_PADS_GPIO_0_27_OFFSET] = 0x5a000008 + 4; // 10mA drive coz we are terminated and want to go faster 50 | # at line 472 in bcm2835gpio.c 51 | 52 | $OPENOCD -c 'interface bcm2835gpio' \ 53 | -c 'transport select jtag' \ 54 | -c 'set _CHIPNAME xc7a35t' \ 55 | -c "bcm2835gpio_peripheral_base ${gpio_peripheral_base_addr}" \ 56 | -c "bcm2835gpio_speed_coeffs ${gpio_speed_coeffs}" \ 57 | -c 'bcm2835gpio_jtag_nums 4 17 27 22' \ 58 | -c 'bcm2835gpio_srst_num 24' \ 59 | -c 'reset_config srst_only' \ 60 | -c 'adapter_khz 6000' \ 61 | -f 'cpld/xilinx-xc7.cfg' \ 62 | -c 'init' \ 63 | -c "pld load 0 $1" \ 64 | -c 'exit' 65 | -------------------------------------------------------------------------------- /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 | 20 | int main(void) 21 | { 22 | irq_setmask(0); 23 | irq_setie(1); 24 | uart_init(); 25 | #ifdef CSR_HDMI_OUT0_I2C_W_ADDR 26 | hdmi_out0_i2c_init(); 27 | #endif 28 | 29 | puts("\nNeTV2 CPU testing software built "__DATE__" "__TIME__); 30 | 31 | config_init(); 32 | time_init(); 33 | 34 | processor_init(); 35 | processor_update(); 36 | processor_start(config_get(CONFIG_KEY_RESOLUTION)); 37 | 38 | ci_prompt(); 39 | 40 | printf( "hdmi_in1_frame_overflow_read %x\n", hdmi_in1_frame_overflow_read() ); 41 | printf( "hdmi_in1_dma_frame_size_read %x\n", hdmi_in1_dma_frame_size_read() ); 42 | printf( "hdmi_in1_dma_slot0_status_read %x\n", hdmi_in1_dma_slot0_status_read() ); 43 | printf( "hdmi_in1_dma_slot0_address_read %x\n", hdmi_in1_dma_slot0_address_read() ); 44 | printf( "hdmi_in1_dma_slot1_status_read %x\n", hdmi_in1_dma_slot1_status_read() ); 45 | printf( "hdmi_in1_dma_slot1_addess_read %x\n", hdmi_in1_dma_slot1_address_read() ); 46 | printf( "hdmi_in1_dma_ev_status_read %x\n", hdmi_in1_dma_ev_status_read() ); 47 | printf( "hdmi_in1_dma_ev_pending_read %x\n", hdmi_in1_dma_ev_pending_read() ); 48 | printf( "hdmi_in1_dma_ev_enable_read %x\n", hdmi_in1_dma_ev_enable_read() ); 49 | 50 | processor_hdmi_out0_source = VIDEO_IN_HDMI_IN1; // this is hard-wired in this scenario 51 | 52 | while(1) { 53 | processor_service(); 54 | ci_service(); 55 | pattern_service(); 56 | } 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /litescope/build.py~: -------------------------------------------------------------------------------- 1 | from litex.gen import * 2 | 3 | from litex.build.tools import write_to_file 4 | from litex.build.generic_platform import * 5 | from litex.build.xilinx.platform import XilinxPlatform 6 | 7 | from litex.soc.integration.soc_core import SoCCore 8 | from litex.soc.cores.uart import UARTWishboneBridge 9 | from litex.soc.integration import cpu_interface 10 | 11 | from litescope import LiteScopeAnalyzer, LiteScopeIO 12 | 13 | 14 | _io = [ 15 | ("clock", 0, Pins(1)), 16 | ("reset", 1, Pins(1)), 17 | ("serial", 0, 18 | Subsignal("tx", Pins(1)), 19 | Subsignal("rx", Pins(1)), 20 | ), 21 | ("bus", 0, Pins(128)), 22 | ("i", 0, Pins(16)), 23 | ("o", 0, Pins(16)) 24 | ] 25 | 26 | class CorePlatform(XilinxPlatform): 27 | def __init__(self): 28 | XilinxPlatform.__init__(self, "", _io) 29 | 30 | 31 | class Core(SoCCore): 32 | platform = CorePlatform() 33 | csr_map = { 34 | "analyzer": 16, 35 | "io": 17 36 | } 37 | csr_map.update(SoCCore.csr_map) 38 | 39 | def __init__(self, platform, clk_freq=int(100e6)): 40 | self.clock_domains.cd_sys = ClockDomain("sys") 41 | self.comb += [ 42 | self.cd_sys.clk.eq(platform.request("clock")), 43 | self.cd_sys.rst.eq(platform.request("reset")) 44 | ] 45 | SoCCore.__init__(self, platform, clk_freq, 46 | cpu_type=None, 47 | csr_data_width=32, 48 | with_uart=False, 49 | with_timer=False 50 | ) 51 | self.add_cpu_or_bridge(UARTWishboneBridge(platform.request("serial"), clk_freq)) 52 | self.add_wb_master(self.cpu_or_bridge.wishbone) 53 | self.submodules.analyzer = LiteScopeAnalyzer(platform.request("bus"), 512) 54 | self.submodules.io = LiteScopeIO(16) 55 | self.comb += [ 56 | self.io.input.eq(platform.request("i")), 57 | platform.request("o").eq(self.io.output), 58 | ] 59 | 60 | 61 | # define platform/core 62 | platform = CorePlatform() 63 | core = Core(platform) 64 | 65 | # generate verilog 66 | v_output = platform.get_verilog(core, name="litescope") 67 | v_output.write("litescope.v") 68 | 69 | # generate csr.csv 70 | memory_regions = core.get_memory_regions() 71 | csr_regions = core.get_csr_regions() 72 | constants = core.get_constants() 73 | write_to_file("csr.csv", cpu_interface.get_csr_csv(csr_regions, constants, memory_regions)) 74 | 75 | # generate analyzer.csv 76 | core.analyzer.export_csv(v_output.ns, "analyzer.csv") 77 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /litescope/build.py: -------------------------------------------------------------------------------- 1 | from litex.gen import * 2 | 3 | from litex.build.tools import write_to_file 4 | from litex.build.generic_platform import * 5 | from litex.build.xilinx.platform import XilinxPlatform 6 | 7 | from litex.soc.integration.soc_core import SoCCore 8 | from litex.soc.cores.uart import UARTWishboneBridge 9 | from litex.soc.integration import cpu_interface 10 | 11 | from litescope import LiteScopeAnalyzer, LiteScopeIO 12 | 13 | 14 | _io = [ 15 | ("clock", 0, Pins(1)), 16 | ("reset", 1, Pins(1)), 17 | ("serial", 0, 18 | Subsignal("tx", Pins(1)), 19 | Subsignal("rx", Pins(1)), 20 | ), 21 | ("bus", 0, Pins(128)), 22 | ("i", 0, Pins(16)), 23 | ("o", 0, Pins(16)) 24 | ] 25 | 26 | class CorePlatform(XilinxPlatform): 27 | def __init__(self): 28 | XilinxPlatform.__init__(self, "", _io) 29 | 30 | 31 | class Core(SoCCore): 32 | platform = CorePlatform() 33 | csr_map = { 34 | "analyzer": 16, 35 | "io": 17 36 | } 37 | csr_map.update(SoCCore.csr_map) 38 | 39 | def __init__(self, platform, clk_freq=int(100e6)): 40 | self.clock_domains.cd_sys = ClockDomain("sys") 41 | self.comb += [ 42 | self.cd_sys.clk.eq(platform.request("clock")), 43 | self.cd_sys.rst.eq(platform.request("reset")) 44 | ] 45 | SoCCore.__init__(self, platform, clk_freq, 46 | cpu_type=None, 47 | csr_data_width=32, 48 | with_uart=False, 49 | with_timer=False 50 | ) 51 | self.add_cpu_or_bridge(UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=3000000)) 52 | self.add_wb_master(self.cpu_or_bridge.wishbone) 53 | self.submodules.analyzer = LiteScopeAnalyzer(platform.request("bus"), 512) 54 | self.submodules.io = LiteScopeIO(16) 55 | self.comb += [ 56 | self.io.input.eq(platform.request("i")), 57 | platform.request("o").eq(self.io.output), 58 | ] 59 | 60 | 61 | # define platform/core 62 | platform = CorePlatform() 63 | core = Core(platform) 64 | 65 | # generate verilog 66 | v_output = platform.get_verilog(core, name="litescope") 67 | v_output.write("litescope.v") 68 | 69 | # generate csr.csv 70 | memory_regions = core.get_memory_regions() 71 | csr_regions = core.get_csr_regions() 72 | constants = core.get_constants() 73 | write_to_file("csr.csv", cpu_interface.get_csr_csv(csr_regions, constants, memory_regions)) 74 | 75 | # generate analyzer.csv 76 | core.analyzer.export_csv(v_output.ns, "analyzer.csv") 77 | 78 | """ 79 | assign videooverlaysoc_litescope_bus = { 80 | videooverlaysoc_videooverlaysoc_videooverlaysoc_ibus_sel[3:0], 81 | 1'b0, 82 | videooverlaysoc_videooverlaysoc_videooverlaysoc_ibus_err, 83 | videooverlaysoc_videooverlaysoc_videooverlaysoc_ibus_ack, 84 | videooverlaysoc_videooverlaysoc_videooverlaysoc_ibus_stb, 85 | videooverlaysoc_hdmi_in1_dma_current_address[23:0], 86 | videooverlaysoc_videooverlaysoc_videooverlaysoc_interrupt[31:0], 87 | videooverlaysoc_videooverlaysoc_videooverlaysoc_ibus_dat_r[31:0], 88 | videooverlaysoc_videooverlaysoc_videooverlaysoc_i_adr_o[31:0] }; 89 | """ 90 | -------------------------------------------------------------------------------- /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 2 | #include 3 | #include 4 | 5 | #include "km.h" 6 | 7 | #define SOURCE 1 8 | #define SINK 0 9 | 10 | unsigned char read_hdcp(unsigned char addr) { 11 | i2c_snoop_edid_snoop_adr_write( addr ); 12 | return( i2c_snoop_edid_snoop_dat_read() ); 13 | } 14 | 15 | #define CHECK_KM 0x86a4df6560c88eLL // panasonic + LG 16 | //#define CHECK_KM 0x225d10cee24175LL // appleTV + LG 17 | #define CHECK 0 18 | 19 | void derive_km(void) { 20 | unsigned int num; 21 | int i; 22 | 23 | unsigned long long source_ksv = 0LL; 24 | unsigned long long sink_ksv = 0LL; 25 | unsigned long long ksv_temp = 0LL; 26 | unsigned long long Km = 0LL; 27 | unsigned long long Kmp = 0LL; 28 | 29 | unsigned long long source_pkey[40]; 30 | unsigned long long sink_pkey[40]; 31 | 32 | hdcp_Km_valid_write(0); 33 | for( i = 0; i < 5; i++ ) { 34 | sink_ksv <<= 8; 35 | sink_ksv |= (read_hdcp(4 - i) & 0xff); 36 | } 37 | 38 | for( i = 0; i < 5; i++ ) { 39 | source_ksv <<= 8; 40 | source_ksv |= (read_hdcp(4 - i + 0x10) & 0xff); 41 | } 42 | 43 | compute_keys( (unsigned long) (source_ksv >> 32), (unsigned long) source_ksv, SOURCE, source_pkey ); 44 | compute_keys( (unsigned long) (sink_ksv >> 32), (unsigned long) sink_ksv, SINK, sink_pkey ); 45 | wprintf( "source public ksv (lsb): %08x\n", (unsigned long) source_ksv ); 46 | wprintf( "source public ksv (msb): %08x\n", (unsigned long) (source_ksv >> 32) ); 47 | wprintf( "sink public ksv (lsb): %08x\n", (unsigned long) sink_ksv ); 48 | wprintf( "sink public ksv (msb): %08x\n", (unsigned long) (sink_ksv >> 32) ); 49 | 50 | ksv_temp = source_ksv; // source Ksv 51 | num = 0; 52 | for( i = 0; i < 40; i++ ) { 53 | if( ksv_temp & 1LL ) { 54 | num++; 55 | Km += sink_pkey[i]; // used to select sink's keys 56 | Km &= 0xFFFFFFFFFFFFFFLL; 57 | // Km %= 72057594037927936LL; 58 | wprintf( "Km 0x%08x", (unsigned long) (Km >> 32) ); 59 | wprintf( "%08x\n", (unsigned long) Km ); 60 | } 61 | ksv_temp >>= 1LL; 62 | } 63 | // printf( "num 1's: %d\n", num ); 64 | // km is the sink km 65 | 66 | ksv_temp = sink_ksv; // sink Ksv 67 | num = 0; 68 | for( i = 0; i < 40; i++ ) { 69 | if( ksv_temp & 1LL ) { 70 | num++; 71 | Kmp += source_pkey[i]; // used to select source's keys 72 | Kmp &= 0xFFFFFFFFFFFFFFLL; 73 | // Kmp %= 72057594037927936LL; 74 | // printf( "Kmp %014llx\n", Kmp ); 75 | } 76 | ksv_temp >>= 1LL; 77 | } 78 | // printf( "num 1's: %d\n", num ); 79 | // Kmp is the source Km 80 | 81 | Km &= 0xFFFFFFFFFFFFFFLL; 82 | Kmp &= 0xFFFFFFFFFFFFFFLL; 83 | 84 | wprintf( "\n" ); 85 | wprintf( "Km (lsb): %08x\n", (unsigned long) (Km & 0xFFFFFFFF) ); 86 | wprintf( "Km (msb): %08x\n", (unsigned long) (Km >> 32) ); 87 | wprintf( "Km'(lsb): %08x\n", (unsigned long) (Kmp & 0xFFFFFFFF) ); 88 | wprintf( "Km'(msb): %08x\n", (unsigned long) (Kmp >> 32) ); 89 | 90 | if( Km != Kmp ) { 91 | wprintf( "Km is not equal to Km', can't encrypt this stream.\n" ); 92 | return; 93 | } 94 | 95 | if( Km == 0 ) { 96 | wprintf( "Km is zero. This probably means derive_km was fired spuriously on disconnect.\n" ); 97 | wprintf( "Aborting without doing anything, since Km = 0 is never a correct condition\n" ); 98 | return; 99 | } else { 100 | wprintf( "Committing Km\n" ); 101 | // now commit Km to the fpga 102 | if( Km != CHECK_KM ) { 103 | wprintf( "*****Km doesn't match check value*****\n" ); 104 | // wprintf( "Writing check Km instead\n" ); 105 | // Km = CHECK_KM; 106 | } 107 | 108 | unsigned char foo; 109 | for( i = 6; i >= 0; i-- ) { 110 | // start with the LSB, which gets written to to the highest CSR address 111 | foo = (unsigned char)(Km & 0xFF); 112 | wprintf( "Writing to %02x to %08x\n", foo, CSR_HDCP_BASE + i * 4); 113 | MMPTR(CSR_HDCP_BASE + i * 4) = foo; 114 | Km >>= 8; 115 | } 116 | 117 | wprintf( "Confirm check Km as writ: (LSB) %08x\n", (unsigned long) hdcp_Km_read() ); 118 | wprintf( "Confirm check Km as writ: (MSB) %08x\n", (unsigned long) (hdcp_Km_read() >> 32) ); 119 | // for( i = 0; i < 7; i++ ) { 120 | // write_km( i, Km & 0xFF ); 121 | // Km >>= 8; 122 | // } 123 | 124 | printf( "Flipping Km valid\n" ); 125 | // indicate Km ready 126 | hdcp_Km_valid_write(1); 127 | //write_km( 7, 1 ); 128 | } 129 | 130 | int last_event; 131 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 132 | ; 133 | } 134 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 135 | ; 136 | } 137 | wprintf( "Invoking HPD\n" ); 138 | hdcp_hpd_ena_write(1); 139 | elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY); 140 | 141 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 142 | ; 143 | } 144 | while( !elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY) ) { 145 | ; 146 | } 147 | wprintf( "Releasing HPD\n" ); 148 | hdcp_hpd_ena_write(0); 149 | } 150 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /gateware/dma.py: -------------------------------------------------------------------------------- 1 | from migen import * 2 | from migen.genlib.cdc import MultiReg, PulseSynchronizer 3 | 4 | from litex.soc.interconnect import stream 5 | from litex.soc.interconnect.csr import * 6 | 7 | from litedram.frontend.dma import LiteDRAMDMAWriter, LiteDRAMDMAReader 8 | 9 | 10 | class DMA(Module): 11 | def __init__(self, mode, dram_port, fifo_depth): 12 | assert mode == dram_port.mode 13 | ashift = log2_int(dram_port.dw//8) 14 | awidth = dram_port.aw + ashift 15 | self.cd = dram_port.cd 16 | 17 | # control 18 | self.enable = Signal(reset=1) # reset to 1 if not used 19 | self.start = Signal(reset=1) # i / reset to 1 if not used 20 | self.idle = Signal() # o 21 | self.slot = Signal() # o 22 | 23 | # parameters 24 | self.slot0_base = Signal(awidth) # in bytes 25 | self.slot1_base = Signal(awidth) # in bytes 26 | self.length = Signal(awidth) # in bytes 27 | 28 | # stream 29 | self.endpoint = endpoint = stream.Endpoint([("data", dram_port.dw)]) 30 | 31 | # # # 32 | 33 | base = Signal(dram_port.aw) 34 | length = Signal(dram_port.aw) 35 | offset = Signal(dram_port.aw) 36 | 37 | # slot selection 38 | self.comb += \ 39 | If(self.slot, 40 | base.eq(self.slot1_base[ashift:]) 41 | ).Else( 42 | base.eq(self.slot0_base[ashift:])) 43 | 44 | # length 45 | self.comb += length.eq(self.length[ashift:]) 46 | 47 | # dma 48 | if mode == "write": 49 | # dma 50 | self.submodules.dma = dma = ResetInserter()(LiteDRAMDMAWriter(dram_port, fifo_depth, True)) 51 | # data 52 | self.comb += dma.sink.data.eq(endpoint.data) 53 | elif mode == "read": 54 | # dma 55 | self.submodules.dma = dma = ResetInserter()(LiteDRAMDMAReader(dram_port, fifo_depth, True)) 56 | # data 57 | self.comb += [ 58 | endpoint.valid.eq(dma.source.valid), 59 | dma.source.ready.eq(endpoint.ready), 60 | endpoint.data.eq(dma.source.data) 61 | ] 62 | 63 | # control 64 | self.submodules.fsm = fsm = FSM(reset_state="IDLE") 65 | fsm.act("IDLE", 66 | self.idle.eq(1), 67 | If(self.enable & self.start, 68 | NextValue(offset, 0), 69 | NextState("RUN") 70 | ) 71 | ) 72 | fsm.act("RUN", 73 | If(mode == "write", 74 | dma.sink.valid.eq(endpoint.valid), 75 | endpoint.ready.eq(dma.sink.ready), 76 | ).Elif(mode == "read", 77 | dma.sink.valid.eq(1), 78 | ), 79 | If(~self.enable, 80 | dma.reset.eq(1), 81 | dram_port.flush.eq(1), 82 | NextState("IDLE") 83 | ).Elif(dma.sink.valid & dma.sink.ready, 84 | NextValue(offset, offset + 1), 85 | If(offset == (length - 1), 86 | NextValue(offset, 0), 87 | NextValue(self.slot, ~self.slot) 88 | ) 89 | ) 90 | ) 91 | self.comb += dma.sink.address.eq(base + offset) 92 | 93 | 94 | class DMAWriter(DMA): 95 | def __init__(self, dram_port, dw=32, fifo_depth=512): 96 | self.sink = stream.Endpoint([("data", dw)]) 97 | 98 | # # # 99 | 100 | DMA.__init__(self, "write", dram_port, fifo_depth) 101 | converter = stream.Converter(dw, dram_port.dw, reverse=False) 102 | self.submodules += converter 103 | self.comb += [ 104 | self.sink.connect(converter.sink), 105 | converter.source.connect(self.endpoint) 106 | ] 107 | 108 | 109 | class DMAReader(DMA): 110 | def __init__(self, dram_port, dw=32, fifo_depth=512): 111 | self.source = stream.Endpoint([("data", dw)]) 112 | 113 | # # # 114 | 115 | DMA.__init__(self, "read", dram_port, fifo_depth) 116 | converter = stream.Converter(dram_port.dw, dw, reverse=False) 117 | self.submodules += converter 118 | self.comb += [ 119 | self.endpoint.connect(converter.sink), 120 | converter.source.connect(self.source) 121 | ] 122 | 123 | 124 | class DMAControl(DMA, AutoCSR): 125 | def __init__(self, dma): 126 | self.enable = CSRStorage() 127 | self.slot0_base = CSRStorage(32) 128 | self.slot1_base = CSRStorage(32) 129 | self.length = CSRStorage(32) 130 | 131 | self.start = CSR() 132 | self.idle = CSRStatus() 133 | self.slot = CSRStatus() 134 | 135 | # # # 136 | 137 | self.specials += [ 138 | MultiReg(self.enable.storage, dma.enable, dma.cd), 139 | MultiReg(self.slot0_base.storage, dma.slot0_base, dma.cd), 140 | MultiReg(self.slot1_base.storage, dma.slot1_base, dma.cd), 141 | MultiReg(self.length.storage, dma.length, dma.cd), 142 | 143 | MultiReg(dma.idle, self.idle.status), 144 | MultiReg(dma.slot, self.slot.status), 145 | ] 146 | 147 | start_sync = PulseSynchronizer("sys", dma.cd) 148 | self.submodules += start_sync 149 | self.comb += [ 150 | start_sync.i.eq(self.start.re), 151 | dma.start.eq(start_sync.o) 152 | ] 153 | 154 | if hasattr(dma, "source"): 155 | self.underflows = CSRStatus(32) 156 | self.sync.pix += [ 157 | If(~dma.source.valid, self.underflows.status.eq(self.underflows.status + 1)) 158 | ] 159 | if hasattr(dma, "sink"): 160 | self.overflows = CSRStatus(32) 161 | self.sync.pix += [ 162 | If(~dma.sink.ready, self.overflows.status.eq(self.overflows.status + 1)) 163 | ] 164 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | # NeTV2 LiteX Quickstart Guide 2 | 3 | 4 | These instructions can be used to quickly start development. 5 | 6 | *tl;dr:* 7 | 8 | 1. Check out this repo with `git clone --recurse-submodules https://github.com/xobs/netv2-fpga.git`. 9 | 1. Ensure you have Python 3.5 or newer installed. 10 | 1. Ensure you have `make` installed. 11 | 1. Download the Risc-V toolchain from https://www.sifive.com/products/tools/ and put it in your PATH. 12 | 1. Go to https://www.xilinx.com/support/download.html and download `All OS installer Single-File Download` 13 | 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` 14 | 1. Go to https://www.xilinx.com/member/forms/license-form.html, get a license, and place it in ~/.Xilinx/Xilinx.lic 15 | 1. Run `./netv2mvp.py` (or `python3 ./netv2mvp.py`) 16 | 17 | ## Updating ## 18 | 19 | To update the repo to the upstream version, including all dependencies, run: 20 | 21 | ```sh 22 | git pull 23 | git submodule update --recursive 24 | ``` 25 | 26 | ## Using lxbuildenv.py Environment ## 27 | 28 | `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: 29 | 30 | ```python 31 | #!/usr/bin/env python3 32 | import lxbuildenv 33 | ``` 34 | 35 | *`lxbuildenv.py` has some very surprising behaviors* that you should be aware of: 36 | 37 | 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*. 38 | 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. 39 | 1. `lxbuildenv` has several command line parameters that it can accept. To display these, run your command with the `--lx-help` parameter. 40 | 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. 41 | 42 | In exchange for some deviation from other build environments, `lxbuildenv` gives you several benefits that come in handy for hardware projects: 43 | 44 | 1. Python dicts enumerate in constant order, giving some consistency to build results. 45 | 1. You will probably be modifying code in the dependencies. By keeping them inside the project directory, this becomes much simpler. 46 | 1. Additionally, all dependencies remain under version control, which you would otherwise lose when installing dependencies as packages. 47 | 1. Hardware, moreso than software, depends on exact version numbers. By using `git` to track dependency versions, this build becomes more reproducible. 48 | 1. It is cross-platform, and works anywhere Xilinx does. 49 | 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. 50 | 51 | ## Working with Dependencies ## 52 | 53 | Dependencies are managed through `git`, and managing their usage is largely an exercise 54 | in working with `git`. 55 | 56 | For example, if you would like to make a change to `litex`, go into `deps/litex` and checkout 57 | a new branch and create a new upstream repo. If you're working on Github, you would do 58 | something like fork the repo to your own organization. 59 | 60 | As an example, assume `sutajiokousagi` has forked upstream `litex`: 61 | 62 | ```sh 63 | $ cd deps/litex 64 | $ git checkout -b new-feature 65 | $ git remote add kosagi git@github.com:sutajiokousagi/litex.git 66 | $ cd - 67 | ``` 68 | 69 | Then, make changes to `deps/litex` as needed. 70 | 71 | When you want to merge changes upstream, go into `deps/litex/` and push the branch to your remote: 72 | 73 | ```sh 74 | $ cd deps/litex 75 | $ git push kosagi new-feature 76 | $ cd - 77 | ``` 78 | 79 | Then you can go and open a Pull Request on Github. 80 | 81 | ## Fetching Updates ## 82 | 83 | Dependencies are designed to be independent, and you should update them as needed. To update a particular 84 | dependency, go into that dependency's subdirectory and run `git pull`. You may also find it easier to 85 | pull updates from a particular dependency and merge them. For example, if you're working on the `new-feature` 86 | branch of `litex` and want to pull changes from upstream, run: 87 | 88 | ```sh 89 | $ cd deps/litex 90 | $ git fetch origin 91 | $ git merge master 92 | $ cd - 93 | ``` 94 | 95 | This will merge all changes from upstream onto your own branch. 96 | 97 | ## Rationale ## 98 | 99 | NeTV2 uses Migen for its HDL, and uses many components from the LiteX project. 100 | These are primarily written in Python, which has a large number of options 101 | available for configuring installs, ranging from global installs, virutlalenv, conda, 102 | as well as several others. Everyone has an opinion on what's right. 103 | 104 | These instructions ignore all of that, in favor of simplicity. You likely already 105 | have a copy of `make` and `python`, and you probably have a compiler 106 | installed for other projects. It's also more challenging to work on submodules 107 | when they're combined together in a `site-packages` repository and outside of version control. 108 | 109 | `lxbuildenv` takes a different approach in that it doubles-down on using native 110 | components and simply modifies several magical environment variables to make 111 | it all work. As a bonus, it works on platforms where Conda doesn't, such as 112 | platforms where packages might not be available. 113 | 114 | ## Support programs ## 115 | 116 | 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`). 117 | 118 | ## Xilinx PATH ## 119 | 120 | 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. 121 | 122 | If not, you can add the Xilinx `bin` directory to your PATH. 123 | 124 | ## PyCharm integration ## 125 | 126 | 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. 127 | 128 | 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. 129 | 130 | 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. 131 | 132 | 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. 133 | 134 | 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. 135 | 136 | ## Visual Studio Code integration ## 137 | 138 | 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: 139 | 140 | ```sh 141 | $ python ./netv2mvp.py --lx-print-env > .env 142 | ``` -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dma_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | 5 | from litex.gen import * 6 | from litex.gen.genlib.resetsync import AsyncResetSynchronizer 7 | 8 | from litex.boards.platforms import arty 9 | 10 | from litex.soc.integration.soc_core import mem_decoder 11 | from litex.soc.integration.soc_sdram import * 12 | from litex.soc.integration.builder import * 13 | from litex.soc.cores import dna, xadc 14 | 15 | from litedram.modules import MT41J128M16 16 | from litedram.phy import a7ddrphy 17 | from litedram.core import ControllerSettings 18 | 19 | from gateware.dma import DMAWriter, DMAReader 20 | 21 | 22 | def csr_map_update(csr_map, csr_peripherals): 23 | csr_map.update(dict((n, v) 24 | for v, n in enumerate(csr_peripherals, start=max(csr_map.values()) + 1))) 25 | 26 | 27 | def period_ns(freq): 28 | return 1e9/freq 29 | 30 | 31 | class CRG(Module): 32 | def __init__(self, platform): 33 | self.clock_domains.cd_sys = ClockDomain() 34 | self.clock_domains.cd_sys4x = ClockDomain(reset_less=True) 35 | self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True) 36 | self.clock_domains.cd_clk200 = ClockDomain() 37 | self.clock_domains.cd_clk50 = ClockDomain() 38 | 39 | clk100 = platform.request("clk100") 40 | rst = ~platform.request("cpu_reset") 41 | 42 | pll_locked = Signal() 43 | pll_fb = Signal() 44 | self.pll_sys = Signal() 45 | pll_sys4x = Signal() 46 | pll_sys4x_dqs = Signal() 47 | pll_clk200 = Signal() 48 | pll_clk50 = Signal() 49 | self.specials += [ 50 | Instance("PLLE2_BASE", 51 | p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, 52 | 53 | # VCO @ 1600 MHz 54 | p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=10.0, 55 | p_CLKFBOUT_MULT=16, p_DIVCLK_DIVIDE=1, 56 | i_CLKIN1=clk100, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, 57 | 58 | # 100 MHz 59 | p_CLKOUT0_DIVIDE=16, p_CLKOUT0_PHASE=0.0, 60 | o_CLKOUT0=self.pll_sys, 61 | 62 | # 400 MHz 63 | p_CLKOUT1_DIVIDE=4, p_CLKOUT1_PHASE=0.0, 64 | o_CLKOUT1=pll_sys4x, 65 | 66 | # 400 MHz dqs 67 | p_CLKOUT2_DIVIDE=4, p_CLKOUT2_PHASE=90.0, 68 | o_CLKOUT2=pll_sys4x_dqs, 69 | 70 | # 200 MHz 71 | p_CLKOUT3_DIVIDE=8, p_CLKOUT3_PHASE=0.0, 72 | o_CLKOUT3=pll_clk200, 73 | 74 | # 50MHz 75 | p_CLKOUT4_DIVIDE=32, p_CLKOUT4_PHASE=0.0, 76 | o_CLKOUT4=pll_clk50 77 | ), 78 | Instance("BUFG", i_I=self.pll_sys, o_O=self.cd_sys.clk), 79 | Instance("BUFG", i_I=pll_sys4x, o_O=self.cd_sys4x.clk), 80 | Instance("BUFG", i_I=pll_sys4x_dqs, o_O=self.cd_sys4x_dqs.clk), 81 | Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk), 82 | Instance("BUFG", i_I=pll_clk50, o_O=self.cd_clk50.clk), 83 | AsyncResetSynchronizer(self.cd_sys, ~pll_locked | rst), 84 | AsyncResetSynchronizer(self.cd_clk200, ~pll_locked | rst), 85 | AsyncResetSynchronizer(self.cd_clk50, ~pll_locked | rst), 86 | ] 87 | 88 | reset_counter = Signal(4, reset=15) 89 | ic_reset = Signal(reset=1) 90 | self.sync.clk200 += \ 91 | If(reset_counter != 0, 92 | reset_counter.eq(reset_counter - 1) 93 | ).Else( 94 | ic_reset.eq(0) 95 | ) 96 | self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) 97 | 98 | 99 | class BaseSoC(SoCSDRAM): 100 | csr_peripherals = { 101 | "ddrphy", 102 | "dna", 103 | "xadc", 104 | } 105 | csr_map_update(SoCSDRAM.csr_map, csr_peripherals) 106 | 107 | def __init__(self, platform, **kwargs): 108 | clk_freq = int(100e6) 109 | SoCSDRAM.__init__(self, platform, clk_freq, 110 | l2_size=32, 111 | integrated_rom_size=0x8000, 112 | integrated_sram_size=0x8000, 113 | ident="Arty DMA Test SoC", 114 | ident_version=True, 115 | reserve_nmi_interrupt=False, 116 | **kwargs) 117 | 118 | self.submodules.crg = CRG(platform) 119 | self.submodules.dna = dna.DNA() 120 | self.submodules.xadc = xadc.XADC() 121 | 122 | self.crg.cd_sys.clk.attr.add("keep") 123 | self.platform.add_period_constraint(self.crg.cd_sys.clk, period_ns(100e6)) 124 | 125 | # sdram 126 | self.submodules.ddrphy = a7ddrphy.A7DDRPHY(platform.request("ddram")) 127 | sdram_module = MT41J128M16(self.clk_freq, "1:4") 128 | self.add_constant("READ_LEVELING_BITSLIP", 3) 129 | self.add_constant("READ_LEVELING_DELAY", 14) 130 | self.register_sdram(self.ddrphy, 131 | sdram_module.geom_settings, 132 | sdram_module.timing_settings, 133 | controller_settings=ControllerSettings(with_bandwidth=True, 134 | cmd_buffer_depth=8, 135 | with_refresh=True)) 136 | 137 | 138 | class DMATestSoC(BaseSoC): 139 | def __init__(self, platform, *args, **kwargs): 140 | BaseSoC.__init__(self, platform, *args, **kwargs) 141 | 142 | # # # 143 | 144 | # parameters 145 | slot_length = 1280*720*32 146 | slot_offset = 0x00000000 147 | slot0_base = slot_offset + 0*slot_length 148 | slot1_base = slot_offset + 1*slot_length 149 | 150 | # create fake pixel clock 151 | self.clock_domains.cd_pix = ClockDomain() # Remove once hdmi in integrated 152 | self.comb += [ 153 | self.cd_pix.clk.eq(ClockSignal()), 154 | self.cd_pix.rst.eq(ResetSignal()) 155 | ] 156 | 157 | # dram dmas 158 | dma_writer = DMAWriter(self.sdram.crossbar.get_port(mode="write", dw=32, cd="pix")) 159 | dma_writer = ClockDomainsRenamer("pix")(dma_writer) 160 | dma_reader = DMAReader(self.sdram.crossbar.get_port(mode="read", dw=32, cd="pix")) 161 | dma_reader = ClockDomainsRenamer("pix")(dma_reader) 162 | self.submodules += dma_writer, dma_reader 163 | 164 | # quick "user manual" :) 165 | # user_sw0 : dma writer enable 166 | # user_sw1 : dma writer valid 167 | # user sw2 : dma reader enable 168 | # user sw3 : dma reader ready 169 | 170 | # user_btn0: dma writer start 171 | # user_btn1: dma_reader start 172 | # user_btn2: error injection 173 | 174 | # user_led0: dma_writer idle 175 | # user_led1: dma_writer ready 176 | # user_led2: dma_reader idle 177 | # user_led3: dma_reader valid 178 | 179 | # test 180 | idata0 = Signal(10) 181 | idata1 = Signal(10) 182 | idata2 = Signal(10) 183 | 184 | self.sync.pix += [ 185 | If(~platform.request("user_btn", 2), 186 | idata0.eq(idata0 + 1), 187 | idata1.eq(idata1 + 2), 188 | idata2.eq(idata2 + 4) 189 | ).Else( 190 | idata0.eq(0), 191 | idata1.eq(0), 192 | idata2.eq(0) 193 | ) 194 | ] 195 | 196 | # dma 197 | self.comb += [ 198 | # control 199 | dma_writer.enable.eq(platform.request("user_sw", 0)), 200 | dma_writer.slot0_base.eq(slot0_base), 201 | dma_writer.slot1_base.eq(slot1_base), 202 | dma_writer.length.eq(slot_length), 203 | 204 | # stream 205 | dma_writer.start.eq(platform.request("user_btn", 0)), 206 | platform.request("user_led", 0).eq(dma_writer.idle), 207 | dma_writer.sink.valid.eq(platform.request("user_sw", 1)), 208 | platform.request("user_led", 1).eq(dma_writer.sink.ready), 209 | dma_writer.sink.data[0:10].eq(idata0), 210 | dma_writer.sink.data[10:20].eq(idata1), 211 | dma_writer.sink.data[20:30].eq(idata2), 212 | ] 213 | 214 | # test 215 | odata0 = Signal(10) 216 | odata1 = Signal(10) 217 | odata2 = Signal(10) 218 | 219 | # hdmi out dma 220 | self.comb += [ 221 | # control 222 | dma_reader.enable.eq(platform.request("user_sw", 2)), 223 | dma_reader.slot0_base.eq(slot0_base), 224 | dma_reader.slot1_base.eq(slot1_base), 225 | dma_reader.length.eq(slot_length), 226 | 227 | # stream 228 | dma_reader.start.eq(platform.request("user_btn", 1)), 229 | platform.request("user_led", 2).eq(dma_reader.idle), 230 | platform.request("user_led", 3).eq(dma_reader.source.valid), 231 | dma_reader.source.ready.eq(platform.request("user_sw", 3)), 232 | odata0.eq(dma_reader.source.data[0:10]), 233 | odata1.eq(dma_reader.source.data[10:20]), 234 | odata2.eq(dma_reader.source.data[20:30]), 235 | ] 236 | 237 | # check 238 | odata0_d = Signal(10) 239 | odata1_d = Signal(10) 240 | odata2_d = Signal(10) 241 | 242 | errors = platform.request("rgb_leds") 243 | 244 | self.sync.pix += [ 245 | errors.r.eq(0b000), 246 | errors.g.eq(0b111), 247 | odata0_d.eq(odata0), 248 | odata1_d.eq(odata1), 249 | odata2_d.eq(odata2), 250 | If(odata0 != (odata0_d + 1), 251 | errors.r[0].eq(1), 252 | errors.g[0].eq(0)), 253 | If(odata1 != (odata1_d + 2), 254 | errors.r[1].eq(1), 255 | errors.g[1].eq(0)), 256 | If(odata2 != (odata2_d + 4), 257 | errors.r[2].eq(1), 258 | errors.g[2].eq(0)), 259 | ] 260 | 261 | def do_exit(self, vns): 262 | pass 263 | 264 | def main(): 265 | platform = arty.Platform() 266 | soc = DMATestSoC(platform) 267 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 268 | vns = builder.build() 269 | soc.do_exit(vns) 270 | 271 | if __name__ == "__main__": 272 | main() 273 | -------------------------------------------------------------------------------- /firmware/hdmi_in0.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "flags.h" 12 | 13 | #ifdef CSR_HDMI_IN0_BASE 14 | 15 | #include "hdmi_in0.h" 16 | 17 | int hdmi_in0_debug; 18 | int hdmi_in0_fb_index; 19 | 20 | #define FRAMEBUFFER_COUNT 4 21 | #define FRAMEBUFFER_MASK (FRAMEBUFFER_COUNT - 1) 22 | 23 | #define HDMI_IN0_FRAMEBUFFERS_BASE (0x00000000 + 0x100000) 24 | #define HDMI_IN0_FRAMEBUFFERS_SIZE (1920*1080*4) 25 | 26 | //#define CLEAN_COMMUTATION 27 | #define DEBUG 28 | 29 | #define HDMI_IN0_PHASE_ADJUST_WER_THRESHOLD 2 30 | 31 | unsigned int hdmi_in0_framebuffer_base(char n) { 32 | return HDMI_IN0_FRAMEBUFFERS_BASE + n*HDMI_IN0_FRAMEBUFFERS_SIZE; 33 | } 34 | 35 | #ifdef HDMI_IN0_INTERRUPT 36 | static int hdmi_in0_fb_slot_indexes[2]; 37 | static int hdmi_in0_next_fb_index; 38 | #endif 39 | 40 | static int hdmi_in0_hres, hdmi_in0_vres; 41 | 42 | extern void processor_update(void); 43 | 44 | #ifdef HDMI_IN0_INTERRUPT 45 | void hdmi_in0_isr(void) 46 | { 47 | int fb_index = -1; 48 | int length; 49 | int expected_length; 50 | unsigned int address_min, address_max; 51 | 52 | printf ("+"); 53 | address_min = HDMI_IN0_FRAMEBUFFERS_BASE & 0x0fffffff; 54 | address_max = address_min + HDMI_IN0_FRAMEBUFFERS_SIZE*FRAMEBUFFER_COUNT; 55 | if((hdmi_in0_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) 56 | && ((hdmi_in0_dma_slot0_address_read() < address_min) || (hdmi_in0_dma_slot0_address_read() > address_max))) 57 | printf("hdmi_in0: slot0: stray DMA\r\n"); 58 | if((hdmi_in0_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) 59 | && ((hdmi_in0_dma_slot1_address_read() < address_min) || (hdmi_in0_dma_slot1_address_read() > address_max))) 60 | printf("hdmi_in0: slot1: stray DMA\r\n"); 61 | 62 | #ifdef CLEAN_COMMUTATION 63 | if((hdmi_in0_resdetection_hres_read() != hdmi_in0_hres) 64 | || (hdmi_in0_resdetection_vres_read() != hdmi_in0_vres)) { 65 | /* Dump frames until we get the expected resolution */ 66 | if(hdmi_in0_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) { 67 | hdmi_in0_dma_slot0_address_write(hdmi_in0_framebuffer_base(hdmi_in0_fb_slot_indexes[0])); 68 | hdmi_in0_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); 69 | } 70 | if(hdmi_in0_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) { 71 | hdmi_in0_dma_slot1_address_write(hdmi_in0_framebuffer_base(hdmi_in0_fb_slot_indexes[1])); 72 | hdmi_in0_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); 73 | } 74 | return; 75 | } 76 | #endif 77 | 78 | expected_length = hdmi_in0_hres*hdmi_in0_vres*2; 79 | if(hdmi_in0_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) { 80 | length = hdmi_in0_dma_slot0_address_read() - (hdmi_in0_framebuffer_base(hdmi_in0_fb_slot_indexes[0]) & 0x0fffffff); 81 | if(length == expected_length) { 82 | fb_index = hdmi_in0_fb_slot_indexes[0]; 83 | hdmi_in0_fb_slot_indexes[0] = hdmi_in0_next_fb_index; 84 | hdmi_in0_next_fb_index = (hdmi_in0_next_fb_index + 1) & FRAMEBUFFER_MASK; 85 | } else { 86 | #ifdef DEBUG 87 | printf("hdmi_in0: slot0: unexpected frame length: %d\r\n", length); 88 | #endif 89 | } 90 | hdmi_in0_dma_slot0_address_write(hdmi_in0_framebuffer_base(hdmi_in0_fb_slot_indexes[0])); 91 | hdmi_in0_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); 92 | } 93 | if(hdmi_in0_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) { 94 | length = hdmi_in0_dma_slot1_address_read() - (hdmi_in0_framebuffer_base(hdmi_in0_fb_slot_indexes[1]) & 0x0fffffff); 95 | if(length == expected_length) { 96 | fb_index = hdmi_in0_fb_slot_indexes[1]; 97 | hdmi_in0_fb_slot_indexes[1] = hdmi_in0_next_fb_index; 98 | hdmi_in0_next_fb_index = (hdmi_in0_next_fb_index + 1) & FRAMEBUFFER_MASK; 99 | } else { 100 | #ifdef DEBUG 101 | printf("hdmi_in0: slot1: unexpected frame length: %d\r\n", length); 102 | #endif 103 | } 104 | hdmi_in0_dma_slot1_address_write(hdmi_in0_framebuffer_base(hdmi_in0_fb_slot_indexes[1])); 105 | hdmi_in0_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); 106 | } 107 | 108 | if(fb_index != -1) 109 | hdmi_in0_fb_index = fb_index; 110 | processor_update(); 111 | } 112 | #endif 113 | 114 | static int hdmi_in0_connected; 115 | static int hdmi_in0_locked; 116 | 117 | void hdmi_in0_init_video(int hres, int vres) 118 | { 119 | hdmi_in0_clocking_mmcm_reset_write(1); 120 | hdmi_in0_connected = hdmi_in0_locked = 0; 121 | hdmi_in0_hres = hres; hdmi_in0_vres = vres; 122 | 123 | #ifdef HDMI_IN0_INTERRUPT 124 | unsigned int mask; 125 | 126 | puts( "setting up HDMI0 interrupts\n" ); 127 | 128 | hdmi_in0_dma_frame_size_write(hres*vres*2); 129 | hdmi_in0_fb_slot_indexes[0] = 0; 130 | hdmi_in0_dma_slot0_address_write(hdmi_in0_framebuffer_base(0)); 131 | hdmi_in0_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); 132 | hdmi_in0_fb_slot_indexes[1] = 1; 133 | hdmi_in0_dma_slot1_address_write(hdmi_in0_framebuffer_base(1)); 134 | hdmi_in0_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); 135 | hdmi_in0_next_fb_index = 2; 136 | 137 | hdmi_in0_dma_ev_pending_write(hdmi_in0_dma_ev_pending_read()); 138 | hdmi_in0_dma_ev_enable_write(0x3); 139 | mask = irq_getmask(); 140 | mask |= 1 << HDMI_IN0_INTERRUPT; 141 | irq_setmask(mask); 142 | 143 | hdmi_in0_fb_index = 3; 144 | #endif 145 | } 146 | 147 | void hdmi_in0_disable(void) 148 | { 149 | #ifdef HDMI_IN0_INTERRUPT 150 | unsigned int mask; 151 | 152 | mask = irq_getmask(); 153 | mask &= ~(1 << HDMI_IN0_INTERRUPT); 154 | irq_setmask(mask); 155 | 156 | hdmi_in0_dma_slot0_status_write(DVISAMPLER_SLOT_EMPTY); 157 | hdmi_in0_dma_slot1_status_write(DVISAMPLER_SLOT_EMPTY); 158 | #endif 159 | hdmi_in0_clocking_mmcm_reset_write(1); 160 | } 161 | 162 | void hdmi_in0_clear_framebuffers(void) 163 | { 164 | int i; 165 | flush_l2_cache(); 166 | volatile unsigned int *framebuffer = (unsigned int *)(MAIN_RAM_BASE + HDMI_IN0_FRAMEBUFFERS_BASE); 167 | for(i=0; i<(HDMI_IN0_FRAMEBUFFERS_SIZE*FRAMEBUFFER_COUNT)/4; i++) { 168 | framebuffer[i] = 0x80108010; /* black in YCbCr 4:2:2*/ 169 | } 170 | } 171 | 172 | static int hdmi_in0_d0, hdmi_in0_d1, hdmi_in0_d2; 173 | 174 | void hdmi_in0_print_status(void) 175 | { 176 | hdmi_in0_data0_wer_update_write(1); 177 | hdmi_in0_data1_wer_update_write(1); 178 | hdmi_in0_data2_wer_update_write(1); 179 | printf("hdmi_in0: ph:%4d %4d %4d // charsync:%d%d%d [%d %d %d] // WER:%3d %3d %3d // chansync:%d // res:%dx%d\r\n", 180 | hdmi_in0_d0, hdmi_in0_d1, hdmi_in0_d2, 181 | hdmi_in0_data0_charsync_char_synced_read(), 182 | hdmi_in0_data1_charsync_char_synced_read(), 183 | hdmi_in0_data2_charsync_char_synced_read(), 184 | hdmi_in0_data0_charsync_ctl_pos_read(), 185 | hdmi_in0_data1_charsync_ctl_pos_read(), 186 | hdmi_in0_data2_charsync_ctl_pos_read(), 187 | hdmi_in0_data0_wer_value_read(), 188 | hdmi_in0_data1_wer_value_read(), 189 | hdmi_in0_data2_wer_value_read(), 190 | hdmi_in0_chansync_channels_synced_read(), 191 | hdmi_in0_resdetection_hres_read(), 192 | hdmi_in0_resdetection_vres_read()); 193 | } 194 | 195 | int hdmi_in0_calibrate_delays(int freq) 196 | { 197 | int i, phase_detector_delay; 198 | 199 | hdmi_in0_data0_cap_dly_ctl_write(DVISAMPLER_DELAY_RST); 200 | hdmi_in0_data1_cap_dly_ctl_write(DVISAMPLER_DELAY_RST); 201 | hdmi_in0_data2_cap_dly_ctl_write(DVISAMPLER_DELAY_RST); 202 | hdmi_in0_data0_cap_phase_reset_write(1); 203 | hdmi_in0_data1_cap_phase_reset_write(1); 204 | hdmi_in0_data2_cap_phase_reset_write(1); 205 | hdmi_in0_d0 = hdmi_in0_d1 = hdmi_in0_d2 = 0; 206 | 207 | /* preload slave phase detector idelay with 90° phase shift 208 | (78 ps taps on 7-series) */ 209 | phase_detector_delay = 10000000/(4*freq*78); 210 | for(i=0; i 3) { 303 | printf("hdmi_in0: giving up\r\n"); 304 | hdmi_in0_calibrate_delays(freq); 305 | return 0; 306 | } 307 | } 308 | } 309 | } 310 | 311 | static void hdmi_in0_check_overflow(void) 312 | { 313 | #ifdef HDMI_IN0_INTERRUPT 314 | if(hdmi_in0_frame_overflow_read()) { 315 | printf("hdmi_in0: FIFO overflow\r\n"); 316 | hdmi_in0_frame_overflow_write(1); 317 | } 318 | #endif 319 | } 320 | 321 | static int hdmi_in0_clocking_locked_filtered(void) 322 | { 323 | static int lock_start_time; 324 | static int lock_status; 325 | 326 | if(hdmi_in0_clocking_locked_read()) { 327 | switch(lock_status) { 328 | case 0: 329 | elapsed(&lock_start_time, -1); 330 | lock_status = 1; 331 | break; 332 | case 1: 333 | if(elapsed(&lock_start_time, SYSTEM_CLOCK_FREQUENCY/4)) 334 | lock_status = 2; 335 | break; 336 | case 2: 337 | return 1; 338 | } 339 | } else 340 | lock_status = 0; 341 | return 0; 342 | } 343 | 344 | static int hdmi_in0_get_wer(void){ 345 | int wer = 0; 346 | wer += hdmi_in0_data0_wer_value_read(); 347 | wer += hdmi_in0_data1_wer_value_read(); 348 | wer += hdmi_in0_data2_wer_value_read(); 349 | return wer; 350 | } 351 | 352 | void hdmi_in0_service(int freq) 353 | { 354 | static int last_event; 355 | 356 | if(hdmi_in0_connected) { 357 | if(!hdmi_in0_edid_hpd_notif_read()) { 358 | if(hdmi_in0_debug) 359 | printf("hdmi_in0: disconnected\r\n"); 360 | hdmi_in0_connected = 0; 361 | hdmi_in0_locked = 0; 362 | hdmi_in0_clocking_mmcm_reset_write(1); 363 | hdmi_in0_clear_framebuffers(); 364 | } else { 365 | if(hdmi_in0_locked) { 366 | if(hdmi_in0_clocking_locked_filtered()) { 367 | if(elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY/2)) { 368 | hdmi_in0_data0_wer_update_write(1); 369 | hdmi_in0_data1_wer_update_write(1); 370 | hdmi_in0_data2_wer_update_write(1); 371 | if(hdmi_in0_debug) 372 | hdmi_in0_print_status(); 373 | if(hdmi_in0_get_wer() >= HDMI_IN0_PHASE_ADJUST_WER_THRESHOLD) 374 | hdmi_in0_adjust_phase(); 375 | } 376 | } else { 377 | if(hdmi_in0_debug) 378 | printf("hdmi_in0: lost PLL lock\r\n"); 379 | hdmi_in0_locked = 0; 380 | hdmi_in0_clear_framebuffers(); 381 | } 382 | } else { 383 | if(hdmi_in0_clocking_locked_filtered()) { 384 | if(hdmi_in0_debug) 385 | printf("hdmi_in0: PLL locked\r\n"); 386 | hdmi_in0_phase_startup(freq); 387 | if(hdmi_in0_debug) 388 | hdmi_in0_print_status(); 389 | hdmi_in0_locked = 1; 390 | } 391 | } 392 | } 393 | } else { 394 | if(hdmi_in0_edid_hpd_notif_read()) { 395 | if(hdmi_in0_debug) 396 | printf("hdmi_in0: connected\r\n"); 397 | hdmi_in0_connected = 1; 398 | hdmi_in0_clocking_mmcm_reset_write(0); 399 | } 400 | } 401 | hdmi_in0_check_overflow(); 402 | } 403 | 404 | #endif 405 | -------------------------------------------------------------------------------- /firmware/hdmi_in1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "flags.h" 12 | 13 | #ifdef CSR_HDMI_IN1_BASE 14 | 15 | #include "hdmi_in1.h" 16 | 17 | int hdmi_in1_debug; 18 | int hdmi_in1_fb_index; 19 | 20 | #define FRAMEBUFFER_COUNT 2 21 | #define FRAMEBUFFER_MASK (FRAMEBUFFER_COUNT - 1) 22 | 23 | //#define HDMI_IN1_FRAMEBUFFERS_BASE (0x00000000 + 0x100000) 24 | #define HDMI_IN1_FRAMEBUFFERS_BASE (0x04018000) 25 | #define HDMI_IN1_FRAMEBUFFERS_SIZE (1920*1080*4) 26 | 27 | //#define CLEAN_COMMUTATION 28 | #define DEBUG 29 | 30 | #define HDMI_IN1_PHASE_ADJUST_WER_THRESHOLD 10 31 | 32 | unsigned int hdmi_in1_framebuffer_base(char n) { 33 | return HDMI_IN1_FRAMEBUFFERS_BASE + n*HDMI_IN1_FRAMEBUFFERS_SIZE; 34 | } 35 | 36 | #ifdef HDMI_IN1_INTERRUPT 37 | static int hdmi_in1_fb_slot_indexes[2]; 38 | static int hdmi_in1_next_fb_index; 39 | #endif 40 | 41 | static int hdmi_in1_hres, hdmi_in1_vres; 42 | 43 | extern void processor_update(void); 44 | 45 | #ifdef HDMI_IN1_INTERRUPT 46 | void hdmi_in1_isr(void) 47 | { 48 | int fb_index = -1; 49 | int length; 50 | int expected_length; 51 | unsigned int address_min, address_max; 52 | 53 | address_min = HDMI_IN1_FRAMEBUFFERS_BASE & 0x0fffffff; 54 | address_max = address_min + HDMI_IN1_FRAMEBUFFERS_SIZE*FRAMEBUFFER_COUNT; 55 | if((hdmi_in1_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) 56 | && ((hdmi_in1_dma_slot0_address_read() < address_min) || (hdmi_in1_dma_slot0_address_read() > address_max))) 57 | printf("hdmi_in1: slot0: stray DMA\r\n"); 58 | if((hdmi_in1_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) 59 | && ((hdmi_in1_dma_slot1_address_read() < address_min) || (hdmi_in1_dma_slot1_address_read() > address_max))) 60 | printf("hdmi_in1: slot1: stray DMA\r\n"); 61 | 62 | #ifdef CLEAN_COMMUTATION 63 | if((hdmi_in1_resdetection_hres_read() != hdmi_in1_hres) 64 | || (hdmi_in1_resdetection_vres_read() != hdmi_in1_vres)) { 65 | /* Dump frames until we get the expected resolution */ 66 | if(hdmi_in1_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) { 67 | hdmi_in1_dma_slot0_address_write(hdmi_in1_framebuffer_base(hdmi_in1_fb_slot_indexes[0])); 68 | hdmi_in1_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); 69 | } 70 | if(hdmi_in1_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) { 71 | hdmi_in1_dma_slot1_address_write(hdmi_in1_framebuffer_base(hdmi_in1_fb_slot_indexes[1])); 72 | hdmi_in1_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); 73 | } 74 | return; 75 | } 76 | #endif 77 | 78 | expected_length = hdmi_in1_hres*hdmi_in1_vres*4; 79 | if(hdmi_in1_dma_slot0_status_read() == DVISAMPLER_SLOT_PENDING) { 80 | length = hdmi_in1_dma_slot0_address_read() - (hdmi_in1_framebuffer_base(hdmi_in1_fb_slot_indexes[0]) & 0x0fffffff); 81 | if(length == expected_length) { 82 | fb_index = hdmi_in1_fb_slot_indexes[0]; 83 | hdmi_in1_fb_slot_indexes[0] = hdmi_in1_next_fb_index; 84 | hdmi_in1_next_fb_index = (hdmi_in1_next_fb_index + 1) & FRAMEBUFFER_MASK; 85 | } else { 86 | #ifdef DEBUG 87 | printf("hdmi_in1: slot0: unexpected frame length: %d\r\n", length); 88 | #endif 89 | } 90 | hdmi_in1_dma_slot0_address_write(hdmi_in1_framebuffer_base(hdmi_in1_fb_slot_indexes[0])); 91 | hdmi_in1_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); 92 | } 93 | if(hdmi_in1_dma_slot1_status_read() == DVISAMPLER_SLOT_PENDING) { 94 | length = hdmi_in1_dma_slot1_address_read() - (hdmi_in1_framebuffer_base(hdmi_in1_fb_slot_indexes[1]) & 0x0fffffff); 95 | if(length == expected_length) { 96 | fb_index = hdmi_in1_fb_slot_indexes[1]; 97 | hdmi_in1_fb_slot_indexes[1] = hdmi_in1_next_fb_index; 98 | hdmi_in1_next_fb_index = (hdmi_in1_next_fb_index + 1) & FRAMEBUFFER_MASK; 99 | } else { 100 | #ifdef DEBUG 101 | printf("hdmi_in1: slot1: unexpected frame length: %d\r\n", length); 102 | #endif 103 | } 104 | hdmi_in1_dma_slot1_address_write(hdmi_in1_framebuffer_base(hdmi_in1_fb_slot_indexes[1])); 105 | hdmi_in1_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); 106 | } 107 | 108 | if(fb_index != -1) 109 | hdmi_in1_fb_index = fb_index; 110 | processor_update(); 111 | 112 | } 113 | #endif 114 | 115 | static int hdmi_in1_connected; 116 | static int hdmi_in1_locked; 117 | 118 | void hdmi_in1_init_video(int hres, int vres) 119 | { 120 | hdmi_in1_clocking_mmcm_reset_write(1); 121 | hdmi_in1_connected = hdmi_in1_locked = 0; 122 | hdmi_in1_hres = hres; hdmi_in1_vres = vres; 123 | 124 | #ifdef HDMI_IN1_INTERRUPT 125 | unsigned int mask; 126 | 127 | printf( "setting up HDMI1 interrupts, hres: %d vres: %d\n", hdmi_in1_hres, hdmi_in1_vres ); 128 | 129 | hdmi_in1_dma_frame_size_write(hres*vres*4); 130 | hdmi_in1_fb_slot_indexes[0] = 0; 131 | hdmi_in1_dma_slot0_address_write(hdmi_in1_framebuffer_base(0)); 132 | printf( "slot0 %x\n", hdmi_in1_framebuffer_base(0) ); 133 | hdmi_in1_dma_slot0_status_write(DVISAMPLER_SLOT_LOADED); 134 | hdmi_in1_fb_slot_indexes[1] = 1; 135 | hdmi_in1_dma_slot1_address_write(hdmi_in1_framebuffer_base(1)); 136 | printf( "slot1 %x\n", hdmi_in1_framebuffer_base(1) ); 137 | hdmi_in1_dma_slot1_status_write(DVISAMPLER_SLOT_LOADED); 138 | hdmi_in1_next_fb_index = 2; 139 | 140 | hdmi_in1_dma_ev_pending_write(hdmi_in1_dma_ev_pending_read()); 141 | hdmi_in1_dma_ev_enable_write(0x3); 142 | mask = irq_getmask(); 143 | mask |= 1 << HDMI_IN1_INTERRUPT; 144 | printf( "irq mask: %x\n", mask ); 145 | irq_setmask(mask); 146 | 147 | hdmi_in1_fb_index = 3; 148 | #endif 149 | } 150 | 151 | void hdmi_in1_disable(void) 152 | { 153 | #ifdef HDMI_IN1_INTERRUPT 154 | unsigned int mask; 155 | 156 | mask = irq_getmask(); 157 | mask &= ~(1 << HDMI_IN1_INTERRUPT); 158 | irq_setmask(mask); 159 | 160 | hdmi_in1_dma_slot0_status_write(DVISAMPLER_SLOT_EMPTY); 161 | hdmi_in1_dma_slot1_status_write(DVISAMPLER_SLOT_EMPTY); 162 | #endif 163 | hdmi_in1_clocking_mmcm_reset_write(1); 164 | } 165 | 166 | void hdmi_in1_clear_framebuffers(void) 167 | { 168 | int i; 169 | flush_l2_cache(); 170 | 171 | volatile unsigned int *framebuffer = (unsigned int *)(MAIN_RAM_BASE + HDMI_IN1_FRAMEBUFFERS_BASE); 172 | for(i=0; i<(HDMI_IN1_FRAMEBUFFERS_SIZE*FRAMEBUFFER_COUNT)/4; i++) { 173 | framebuffer[i] = 0x80108010; /* black in YCbCr 4:2:2*/ 174 | } 175 | } 176 | 177 | static int hdmi_in1_d0, hdmi_in1_d1, hdmi_in1_d2; 178 | 179 | void hdmi_in1_print_status(void) 180 | { 181 | hdmi_in1_data0_wer_update_write(1); 182 | hdmi_in1_data1_wer_update_write(1); 183 | hdmi_in1_data2_wer_update_write(1); 184 | printf("hdmi_in1: ph:%4d %4d %4d // charsync:%d%d%d [%d %d %d] // WER:%3d %3d %3d // chansync:%d // res:%dx%d\r\n", 185 | hdmi_in1_d0, hdmi_in1_d1, hdmi_in1_d2, 186 | hdmi_in1_data0_charsync_char_synced_read(), 187 | hdmi_in1_data1_charsync_char_synced_read(), 188 | hdmi_in1_data2_charsync_char_synced_read(), 189 | hdmi_in1_data0_charsync_ctl_pos_read(), 190 | hdmi_in1_data1_charsync_ctl_pos_read(), 191 | hdmi_in1_data2_charsync_ctl_pos_read(), 192 | hdmi_in1_data0_wer_value_read(), 193 | hdmi_in1_data1_wer_value_read(), 194 | hdmi_in1_data2_wer_value_read(), 195 | hdmi_in1_chansync_channels_synced_read(), 196 | hdmi_in1_resdetection_hres_read(), 197 | hdmi_in1_resdetection_vres_read()); 198 | } 199 | 200 | int hdmi_in1_calibrate_delays(int freq) 201 | { 202 | int i, phase_detector_delay; 203 | 204 | hdmi_in1_data0_cap_dly_ctl_write(DVISAMPLER_DELAY_RST); 205 | hdmi_in1_data1_cap_dly_ctl_write(DVISAMPLER_DELAY_RST); 206 | hdmi_in1_data2_cap_dly_ctl_write(DVISAMPLER_DELAY_RST); 207 | hdmi_in1_data0_cap_phase_reset_write(1); 208 | hdmi_in1_data1_cap_phase_reset_write(1); 209 | hdmi_in1_data2_cap_phase_reset_write(1); 210 | hdmi_in1_d0 = hdmi_in1_d1 = hdmi_in1_d2 = 0; 211 | 212 | /* preload slave phase detector idelay with 90° phase shift 213 | (78 ps taps on 7-series) */ 214 | phase_detector_delay = 10000000/(4*freq*78); 215 | for(i=0; i 3) { 308 | printf("hdmi_in1: giving up\r\n"); 309 | hdmi_in1_calibrate_delays(freq); 310 | return 0; 311 | } 312 | } 313 | } 314 | } 315 | 316 | static void hdmi_in1_check_overflow(void) 317 | { 318 | #ifdef HDMI_IN1_INTERRUPT 319 | if(hdmi_in1_frame_overflow_read()) { 320 | printf("hdmi_in1: FIFO overflow\r\n"); 321 | hdmi_in1_frame_overflow_write(1); 322 | } 323 | #endif 324 | } 325 | 326 | static int hdmi_in1_clocking_locked_filtered(void) 327 | { 328 | static int lock_start_time; 329 | static int lock_status; 330 | 331 | if(hdmi_in1_clocking_locked_read()) { 332 | switch(lock_status) { 333 | case 0: 334 | elapsed(&lock_start_time, -1); 335 | lock_status = 1; 336 | break; 337 | case 1: 338 | if(elapsed(&lock_start_time, SYSTEM_CLOCK_FREQUENCY/4)) 339 | lock_status = 2; 340 | break; 341 | case 2: 342 | return 1; 343 | } 344 | } else 345 | lock_status = 0; 346 | return 0; 347 | } 348 | 349 | static int hdmi_in1_get_wer(void){ 350 | int wer = 0; 351 | wer += hdmi_in1_data0_wer_value_read(); 352 | wer += hdmi_in1_data1_wer_value_read(); 353 | wer += hdmi_in1_data2_wer_value_read(); 354 | return wer; 355 | } 356 | 357 | void hdmi_in1_service(int freq) 358 | { 359 | static int last_event; 360 | 361 | if(hdmi_in1_connected) { 362 | if(!hdmi_in1_edid_hpd_notif_read()) { 363 | if(hdmi_in1_debug) 364 | printf("hdmi_in1: disconnected\r\n"); 365 | hdmi_in1_connected = 0; 366 | hdmi_in1_locked = 0; 367 | hdmi_in1_clocking_mmcm_reset_write(1); 368 | hdmi_in1_clear_framebuffers(); 369 | } else { 370 | if(hdmi_in1_locked) { 371 | if(hdmi_in1_clocking_locked_filtered()) { 372 | if(elapsed(&last_event, SYSTEM_CLOCK_FREQUENCY/2)) { 373 | hdmi_in1_data0_wer_update_write(1); 374 | hdmi_in1_data1_wer_update_write(1); 375 | hdmi_in1_data2_wer_update_write(1); 376 | if(hdmi_in1_debug) 377 | hdmi_in1_print_status(); 378 | if(hdmi_in1_get_wer() >= HDMI_IN1_PHASE_ADJUST_WER_THRESHOLD) 379 | hdmi_in1_adjust_phase(); 380 | } 381 | } else { 382 | if(hdmi_in1_debug) 383 | printf("hdmi_in1: lost PLL lock\r\n"); 384 | hdmi_in1_locked = 0; 385 | hdmi_in1_clear_framebuffers(); 386 | } 387 | } else { 388 | if(hdmi_in1_clocking_locked_filtered()) { 389 | if(hdmi_in1_debug) 390 | printf("hdmi_in1: PLL locked\r\n"); 391 | hdmi_in1_phase_startup(freq); 392 | if(hdmi_in1_debug) 393 | hdmi_in1_print_status(); 394 | hdmi_in1_locked = 1; 395 | } 396 | } 397 | } 398 | } else { 399 | if(hdmi_in1_edid_hpd_notif_read()) { 400 | if(hdmi_in1_debug) 401 | printf("hdmi_in1: connected\r\n"); 402 | hdmi_in1_connected = 1; 403 | hdmi_in1_clocking_mmcm_reset_write(0); 404 | } 405 | } 406 | hdmi_in1_check_overflow(); 407 | } 408 | 409 | #endif 410 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /netv2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import os 4 | 5 | from litex.gen import * 6 | from litex.gen.genlib.resetsync import AsyncResetSynchronizer 7 | 8 | from litex.build.generic_platform import * 9 | from litex.build.xilinx import XilinxPlatform 10 | 11 | from litex.soc.integration.soc_core import mem_decoder 12 | from litex.soc.integration.soc_sdram import * 13 | from litex.soc.integration.builder import * 14 | from litex.soc.cores import dna, xadc 15 | from litex.soc.cores.frequency_meter import FrequencyMeter 16 | 17 | from litedram.modules import MT41J128M16 18 | from litedram.phy import a7ddrphy 19 | from litedram.core import ControllerSettings 20 | 21 | from litepcie.phy.s7pciephy import S7PCIEPHY 22 | from litepcie.core import LitePCIeEndpoint, LitePCIeMSI 23 | from litepcie.frontend.dma import LitePCIeDMA 24 | from litepcie.frontend.wishbone import LitePCIeWishboneBridge 25 | 26 | from litevideo.input import HDMIIn 27 | from litevideo.output import VideoOut 28 | 29 | import cpu_interface 30 | 31 | 32 | _io = [ 33 | ("clk50", 0, Pins("R2"), IOStandard("LVCMOS33")), 34 | 35 | ("user_led", 0, Pins("U2"), IOStandard("LVCMOS33")), 36 | 37 | ("serial", 0, 38 | Subsignal("tx", Pins("M5")), 39 | Subsignal("rx", Pins("N6")), 40 | IOStandard("LVCMOS33"), 41 | ), 42 | 43 | ("ddram", 0, 44 | Subsignal("a", Pins( 45 | "U15 M17 N18 U16 R18 P18 T18 T17", 46 | "U17 N16 R16 N17 V17 R17"), 47 | IOStandard("SSTL15")), 48 | Subsignal("ba", Pins("T15 M16 P15"), IOStandard("SSTL15")), 49 | Subsignal("ras_n", Pins("L18"), IOStandard("SSTL15")), 50 | Subsignal("cas_n", Pins("K17"), IOStandard("SSTL15")), 51 | Subsignal("we_n", Pins("P16"), IOStandard("SSTL15")), 52 | Subsignal("dm", Pins("D9 B14 F14 C18"), IOStandard("SSTL15")), 53 | Subsignal("dq", Pins( 54 | "D11 B11 D8 C11 C8 B10 C9 A10 " 55 | "A15 A14 E13 B12 C13 A12 D13 A13 " 56 | "H18 G17 G16 F17 G14 E18 H16 H17 " 57 | "C17 D16 B17 E16 C16 E17 D15 D18 " 58 | ), 59 | IOStandard("SSTL15"), 60 | Misc("IN_TERM=UNTUNED_SPLIT_50")), 61 | Subsignal("dqs_p", Pins("B9 C14 G15 B16"), IOStandard("DIFF_SSTL15")), 62 | Subsignal("dqs_n", Pins("A9 B15 F15 A17"), IOStandard("DIFF_SSTL15")), 63 | Subsignal("clk_p", Pins("P14"), IOStandard("DIFF_SSTL15")), 64 | Subsignal("clk_n", Pins("R15"), IOStandard("DIFF_SSTL15")), 65 | Subsignal("cke", Pins("K15"), IOStandard("SSTL15")), 66 | Subsignal("odt", Pins("K18"), IOStandard("SSTL15")), 67 | Subsignal("reset_n", Pins("V16"), IOStandard("LVCMOS15")), 68 | Subsignal("cs_n", Pins("J16"), IOStandard("SSTL15")), 69 | Misc("SLEW=FAST"), 70 | ), 71 | 72 | ("pcie_x1", 0, 73 | Subsignal("rst_n", Pins("N1"), IOStandard("LVCMOS33")), 74 | Subsignal("clk_p", Pins("D6")), 75 | Subsignal("clk_n", Pins("D5")), 76 | Subsignal("rx_p", Pins("E4")), 77 | Subsignal("rx_n", Pins("E3")), 78 | Subsignal("tx_p", Pins("H2")), 79 | Subsignal("tx_n", Pins("H1")) 80 | ), 81 | 82 | ("hdmi_in", 0, 83 | Subsignal("clk_p", Pins("P4"), IOStandard("TMDS_33")), 84 | Subsignal("clk_n", Pins("P3"), IOStandard("TMDS_33")), 85 | Subsignal("data0_p", Pins("U4"), IOStandard("TMDS_33")), 86 | Subsignal("data0_n", Pins("V4"), IOStandard("TMDS_33")), 87 | Subsignal("data1_p", Pins("P6"), IOStandard("TMDS_33")), 88 | Subsignal("data1_n", Pins("P5"), IOStandard("TMDS_33")), 89 | Subsignal("data2_p", Pins("R7"), IOStandard("TMDS_33")), 90 | Subsignal("data2_n", Pins("T7"), IOStandard("TMDS_33")), 91 | Subsignal("scl", Pins("K5"), IOStandard("LVCMOS33")), # FPIO5 (not connected) 92 | Subsignal("sda", Pins("J5"), IOStandard("LVCMOS33")), # FPIO4 (not connected) 93 | ), 94 | 95 | ("hdmi_out", 0, 96 | Subsignal("clk_p", Pins("R3"), IOStandard("TMDS_33")), 97 | Subsignal("clk_n", Pins("T2"), IOStandard("TMDS_33")), 98 | Subsignal("data0_p", Pins("T4"), IOStandard("TMDS_33")), 99 | Subsignal("data0_n", Pins("T3"), IOStandard("TMDS_33")), 100 | Subsignal("data1_p", Pins("U6"), IOStandard("TMDS_33")), 101 | Subsignal("data1_n", Pins("U5"), IOStandard("TMDS_33")), 102 | Subsignal("data2_p", Pins("V7"), IOStandard("TMDS_33")), 103 | Subsignal("data2_n", Pins("V6"), IOStandard("TMDS_33")), 104 | ), 105 | 106 | ("hdmi_sda_over_up", 0, Pins("V7"), IOStandard("LVCMOS33")), 107 | ("hdmi_sda_over_dn", 0, Pins("R6"), IOStandard("LVCMOS33")), 108 | ("hdmi_hdp_over", 0, Pins("V8"), IOStandard("LVCMOS33")), 109 | 110 | ] 111 | 112 | 113 | class Platform(XilinxPlatform): 114 | def __init__(self, toolchain="vivado", programmer="vivado"): 115 | XilinxPlatform.__init__(self, "xc7a50t-csg325-2", _io, 116 | toolchain=toolchain) 117 | 118 | self.add_platform_command( 119 | "set_property CONFIG_VOLTAGE 1.5 [current_design]") 120 | self.add_platform_command( 121 | "set_property CFGBVS GND [current_design]") 122 | self.add_platform_command( 123 | "set_property BITSTREAM.CONFIG.CONFIGRATE 22 [current_design]") 124 | self.add_platform_command( 125 | "set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]") 126 | self.toolchain.bitstream_commands = [ 127 | "set_property CONFIG_VOLTAGE 1.5 [current_design]", 128 | "set_property CFGBVS GND [current_design]", 129 | "set_property BITSTREAM.CONFIG.CONFIGRATE 22 [current_design]", 130 | "set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 1 [current_design]", 131 | ] 132 | self.toolchain.additional_commands = \ 133 | ["write_cfgmem -verbose -force -format bin -interface spix1 -size 64 " 134 | "-loadbit \"up 0x0 {build_name}.bit\" -file {build_name}.bin"] 135 | self.programmer = programmer 136 | 137 | self.add_platform_command(""" 138 | create_clock -name pcie_phy_clk -period 10.0 [get_pins {{pcie_phy/pcie_support_i/pcie_i/inst/inst/gt_top_i/pipe_wrapper_i/pipe_lane[0].gt_wrapper_i/gtp_channel.gtpe2_channel_i/TXOUTCLK}}] 139 | """) 140 | 141 | def create_programmer(self): 142 | if self.programmer == "vivado": 143 | return VivadoProgrammer(flash_part="n25q128-3.3v-spi-x1_x2_x4") 144 | else: 145 | raise ValueError("{} programmer is not supported" 146 | .format(self.programmer)) 147 | 148 | def do_finalize(self, fragment): 149 | XilinxPlatform.do_finalize(self, fragment) 150 | 151 | 152 | def csr_map_update(csr_map, csr_peripherals): 153 | csr_map.update(dict((n, v) 154 | for v, n in enumerate(csr_peripherals, start=max(csr_map.values()) + 1))) 155 | 156 | 157 | def period_ns(freq): 158 | return 1e9/freq 159 | 160 | 161 | class CRG(Module): 162 | def __init__(self, platform): 163 | self.clock_domains.cd_sys = ClockDomain() 164 | self.clock_domains.cd_sys4x = ClockDomain(reset_less=True) 165 | self.clock_domains.cd_sys4x_dqs = ClockDomain(reset_less=True) 166 | self.clock_domains.cd_clk200 = ClockDomain() 167 | self.clock_domains.cd_clk100 = ClockDomain() 168 | 169 | clk50 = platform.request("clk50") 170 | rst = Signal() 171 | 172 | pll_locked = Signal() 173 | pll_fb = Signal() 174 | self.pll_sys = Signal() 175 | pll_sys4x = Signal() 176 | pll_sys4x_dqs = Signal() 177 | pll_clk200 = Signal() 178 | self.specials += [ 179 | Instance("PLLE2_BASE", 180 | p_STARTUP_WAIT="FALSE", o_LOCKED=pll_locked, 181 | 182 | # VCO @ 1600 MHz 183 | p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=20.0, 184 | p_CLKFBOUT_MULT=32, p_DIVCLK_DIVIDE=1, 185 | i_CLKIN1=clk50, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb, 186 | 187 | # 100 MHz 188 | p_CLKOUT0_DIVIDE=16, p_CLKOUT0_PHASE=0.0, 189 | o_CLKOUT0=self.pll_sys, 190 | 191 | # 400 MHz 192 | p_CLKOUT1_DIVIDE=4, p_CLKOUT1_PHASE=0.0, 193 | o_CLKOUT1=pll_sys4x, 194 | 195 | # 400 MHz dqs 196 | p_CLKOUT2_DIVIDE=4, p_CLKOUT2_PHASE=90.0, 197 | o_CLKOUT2=pll_sys4x_dqs, 198 | 199 | # 200 MHz 200 | p_CLKOUT3_DIVIDE=8, p_CLKOUT3_PHASE=0.0, 201 | o_CLKOUT3=pll_clk200 202 | ), 203 | Instance("BUFG", i_I=self.pll_sys, o_O=self.cd_sys.clk), 204 | Instance("BUFG", i_I=self.pll_sys, o_O=self.cd_clk100.clk), 205 | Instance("BUFG", i_I=pll_clk200, o_O=self.cd_clk200.clk), 206 | Instance("BUFG", i_I=pll_sys4x, o_O=self.cd_sys4x.clk), 207 | Instance("BUFG", i_I=pll_sys4x_dqs, o_O=self.cd_sys4x_dqs.clk), 208 | AsyncResetSynchronizer(self.cd_sys, ~pll_locked | rst), 209 | AsyncResetSynchronizer(self.cd_clk200, ~pll_locked | rst), 210 | AsyncResetSynchronizer(self.cd_clk100, ~pll_locked | rst) 211 | ] 212 | 213 | reset_counter = Signal(4, reset=15) 214 | ic_reset = Signal(reset=1) 215 | self.sync.clk200 += \ 216 | If(reset_counter != 0, 217 | reset_counter.eq(reset_counter - 1) 218 | ).Else( 219 | ic_reset.eq(0) 220 | ) 221 | self.specials += Instance("IDELAYCTRL", i_REFCLK=ClockSignal("clk200"), i_RST=ic_reset) 222 | 223 | 224 | class BaseSoC(SoCSDRAM): 225 | csr_peripherals = { 226 | "ddrphy", 227 | "dna", 228 | "xadc", 229 | } 230 | csr_map_update(SoCSDRAM.csr_map, csr_peripherals) 231 | 232 | def __init__(self, platform, **kwargs): 233 | clk_freq = int(100e6) 234 | SoCSDRAM.__init__(self, platform, clk_freq, 235 | integrated_rom_size=0x8000, 236 | integrated_sram_size=0x8000, 237 | ident="NeTV2 LiteX Base SoC", 238 | reserve_nmi_interrupt=False, 239 | **kwargs) 240 | 241 | self.submodules.crg = CRG(platform) 242 | self.submodules.dna = dna.DNA() 243 | self.submodules.xadc = xadc.XADC() 244 | 245 | self.crg.cd_sys.clk.attr.add("keep") 246 | self.platform.add_period_constraint(self.crg.cd_sys.clk, period_ns(100e6)) 247 | 248 | # sdram 249 | self.submodules.ddrphy = a7ddrphy.A7DDRPHY(platform.request("ddram")) 250 | sdram_module = MT41J128M16(self.clk_freq, "1:4") 251 | self.add_constant("READ_LEVELING_BITSLIP", 3) 252 | self.add_constant("READ_LEVELING_DELAY", 14) 253 | self.register_sdram(self.ddrphy, 254 | sdram_module.geom_settings, 255 | sdram_module.timing_settings, 256 | controller_settings=ControllerSettings(with_bandwidth=True, 257 | cmd_buffer_depth=8, 258 | with_refresh=True)) 259 | 260 | # common led 261 | self.sys_led = Signal() 262 | self.pcie_led = Signal() 263 | self.comb += platform.request("user_led", 0).eq(self.sys_led ^ self.pcie_led) 264 | 265 | # sys led 266 | sys_counter = Signal(32) 267 | self.sync += sys_counter.eq(sys_counter + 1) 268 | self.comb += self.sys_led.eq(sys_counter[26]) 269 | 270 | 271 | class PCIeSoC(BaseSoC): 272 | csr_map = { 273 | "pcie_phy": 20, 274 | "dma": 21, 275 | "msi": 22, 276 | } 277 | csr_map.update(BaseSoC.csr_map) 278 | 279 | BaseSoC.mem_map["csr"] = 0x00000000 280 | BaseSoC.mem_map["rom"] = 0x20000000 281 | 282 | def __init__(self, platform, **kwargs): 283 | BaseSoC.__init__(self, platform, csr_data_width=32, **kwargs) 284 | 285 | # pcie phy 286 | self.submodules.pcie_phy = S7PCIEPHY(platform, platform.request("pcie_x1")) 287 | self.platform.add_false_path_constraints( 288 | self.crg.cd_sys.clk, 289 | self.pcie_phy.cd_pcie.clk) 290 | 291 | # pcie endpoint 292 | self.submodules.pcie_endpoint = LitePCIeEndpoint(self.pcie_phy, with_reordering=True) 293 | 294 | # pcie wishbone bridge 295 | self.submodules.pcie_wishbone = LitePCIeWishboneBridge(self.pcie_endpoint, lambda a: 1) 296 | self.add_wb_master(self.pcie_wishbone.wishbone) 297 | 298 | # pcie dma 299 | self.submodules.dma = LitePCIeDMA(self.pcie_phy, self.pcie_endpoint, with_loopback=True) 300 | self.dma.source.connect(self.dma.sink) 301 | 302 | # pcie msi 303 | self.submodules.msi = LitePCIeMSI() 304 | self.comb += self.msi.source.connect(self.pcie_phy.msi) 305 | self.interrupts = { 306 | "DMA_WRITER": self.dma.writer.irq, 307 | "DMA_READER": self.dma.reader.irq 308 | } 309 | for i, (k, v) in enumerate(sorted(self.interrupts.items())): 310 | self.comb += self.msi.irqs[i].eq(v) 311 | self.add_constant(k + "_INTERRUPT", i) 312 | 313 | # pcie led 314 | pcie_counter = Signal(32) 315 | self.sync.pcie += pcie_counter.eq(pcie_counter + 1) 316 | self.comb += self.pcie_led.eq(pcie_counter[26]) 317 | 318 | def generate_software_header(self): 319 | csr_header = get_csr_header(self.get_csr_regions(), 320 | self.get_constants(), 321 | with_access_functions=False) 322 | tools.write_to_file(os.path.join("software", "pcie", "kernel", "csr.h"), csr_header) 323 | 324 | class VideoSoC(BaseSoC): 325 | csr_peripherals = { 326 | "hdmi_out0", 327 | "hdmi_in0", 328 | "hdmi_in0_freq", 329 | "hdmi_in0_edid_mem" 330 | } 331 | csr_map_update(BaseSoC.csr_map, csr_peripherals) 332 | 333 | interrupt_map = { 334 | "hdmi_in0": 3, 335 | } 336 | interrupt_map.update(BaseSoC.interrupt_map) 337 | 338 | def __init__(self, platform, *args, **kwargs): 339 | BaseSoC.__init__(self, platform, *args, **kwargs) 340 | 341 | # # # 342 | 343 | pix_freq = 148.50e6 344 | 345 | # hdmi in 346 | hdmi_in0_pads = platform.request("hdmi_in") 347 | self.submodules.hdmi_in0_freq = FrequencyMeter(period=self.clk_freq) 348 | self.submodules.hdmi_in0 = HDMIIn(hdmi_in0_pads, 349 | self.sdram.crossbar.get_port(mode="write"), 350 | fifo_depth=512, 351 | device="xc7") 352 | self.comb += self.hdmi_in0_freq.clk.eq(self.hdmi_in0.clocking.cd_pix.clk) 353 | self.platform.add_period_constraint(self.hdmi_in0.clocking.cd_pix.clk, period_ns(1*pix_freq)) 354 | self.platform.add_period_constraint(self.hdmi_in0.clocking.cd_pix1p25x.clk, period_ns(1.25*pix_freq)) 355 | self.platform.add_period_constraint(self.hdmi_in0.clocking.cd_pix5x.clk, period_ns(5*pix_freq)) 356 | 357 | self.platform.add_false_path_constraints( 358 | self.crg.cd_sys.clk, 359 | self.hdmi_in0.clocking.cd_pix.clk, 360 | self.hdmi_in0.clocking.cd_pix1p25x.clk, 361 | self.hdmi_in0.clocking.cd_pix5x.clk) 362 | 363 | # hdmi out 364 | hdmi_out0_dram_port = self.sdram.crossbar.get_port(mode="read", dw=16, cd="hdmi_out0_pix", reverse=True) 365 | self.submodules.hdmi_out0 = VideoOut(platform.device, 366 | platform.request("hdmi_out"), 367 | hdmi_out0_dram_port, 368 | "ycbcr422", 369 | fifo_depth=4096) 370 | 371 | self.platform.add_period_constraint(self.hdmi_out0.driver.clocking.cd_pix.clk, period_ns(1*pix_freq)) 372 | self.platform.add_period_constraint(self.hdmi_out0.driver.clocking.cd_pix5x.clk, period_ns(5*pix_freq)) 373 | 374 | self.platform.add_false_path_constraints( 375 | self.crg.cd_sys.clk, 376 | self.hdmi_out0.driver.clocking.cd_pix.clk, 377 | self.hdmi_out0.driver.clocking.cd_pix5x.clk) 378 | 379 | # hdmi over 380 | self.comb += [ 381 | platform.request("hdmi_sda_over_up").eq(0), 382 | platform.request("hdmi_sda_over_dn").eq(0), 383 | platform.request("hdmi_hdp_over").eq(0), 384 | ] 385 | 386 | 387 | def main(): 388 | platform = Platform() 389 | if len(sys.argv) < 2: 390 | print("missing target (base or pcie or video)") 391 | exit() 392 | if sys.argv[1] == "base": 393 | soc = BaseSoC(platform) 394 | elif sys.argv[1] == "pcie": 395 | soc = PCIeSoC(platform) 396 | elif sys.argv[1] == "video": 397 | soc = VideoSoC(platform) 398 | builder = Builder(soc, output_dir="build", csr_csv="test/csr.csv") 399 | vns = builder.build() 400 | 401 | if sys.argv[1] == "pcie": 402 | soc.generate_software_header() 403 | 404 | if __name__ == "__main__": 405 | main() 406 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------