From 6366fbb5a79453c8e7a4a504676794389260c728 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 26 Jan 2026 16:42:21 +1100 Subject: [PATCH 01/10] arty a7: Simplify GPIO connections Currently, GPIO lines 0 - 8 drive three of the 3-colour LEDs on output, but on input read the state of the pins labelled IO10 - IO13, IO26 - IO29 and IO8 on the Arty board. Then GPIO lines 10 - 17 drive IO10 - IO13 and IO26 - IO29 on output, but on input read the 4 buttons and 4 switches. To simplify all this and prepare for future changes, this just detaches IO8, IO13 - IO13 and IO26 - IO29, so now GPIO 0 - 8 read 0 on input, and GPIO 10 - 17 do nothing on output. Signed-off-by: Paul Mackerras --- fpga/arty_a7.xdc | 34 +++++++-------- fpga/top-arty.vhdl | 102 +++++++++++++++++---------------------------- 2 files changed, 56 insertions(+), 80 deletions(-) diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index cf90805..df55577 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -150,24 +150,24 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard/sdpa # Arduino/chipKIT shield connector ################################################################################ -set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io0 }]; -set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io1 }]; -set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io2 }]; -set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io3 }]; -set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io4 }]; -set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io5 }]; -set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io6 }]; -set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io7 }]; -set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io8 }]; +#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io0 }]; +#set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io1 }]; +#set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io2 }]; +#set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io3 }]; +#set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io4 }]; +#set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io5 }]; +#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io6 }]; +#set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io7 }]; +#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io8 }]; set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io9 }]; -set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io10 }]; -set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io11 }]; -set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io12 }]; -set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io13 }]; -set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io26 }]; -set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io27 }]; -set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io28 }]; -set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io29 }]; +#set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io10 }]; +#set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io11 }]; +#set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io12 }]; +#set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io13 }]; +#set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io26 }]; +#set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io27 }]; +#set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io28 }]; +#set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io29 }]; set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io30 }]; set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io31 }]; set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io32 }]; diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index e86cecd..54d1f90 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -68,24 +68,24 @@ entity toplevel is sw3 : in std_ulogic; -- GPIO - shield_io0 : inout std_ulogic; - shield_io1 : inout std_ulogic; - shield_io2 : inout std_ulogic; - shield_io3 : inout std_ulogic; - shield_io4 : inout std_ulogic; - shield_io5 : inout std_ulogic; - shield_io6 : inout std_ulogic; - shield_io7 : inout std_ulogic; - shield_io8 : inout std_ulogic; + --shield_io0 : inout std_ulogic; + --shield_io1 : inout std_ulogic; + --shield_io2 : inout std_ulogic; + --shield_io3 : inout std_ulogic; + --shield_io4 : inout std_ulogic; + --shield_io5 : inout std_ulogic; + --shield_io6 : inout std_ulogic; + --shield_io7 : inout std_ulogic; + --shield_io8 : inout std_ulogic; shield_io9 : inout std_ulogic; - shield_io10 : inout std_ulogic; - shield_io11 : inout std_ulogic; - shield_io12 : inout std_ulogic; - shield_io13 : inout std_ulogic; - shield_io26 : inout std_ulogic; - shield_io27 : inout std_ulogic; - shield_io28 : inout std_ulogic; - shield_io29 : inout std_ulogic; + --shield_io10 : inout std_ulogic; + --shield_io11 : inout std_ulogic; + --shield_io12 : inout std_ulogic; + --shield_io13 : inout std_ulogic; + --shield_io26 : inout std_ulogic; + --shield_io27 : inout std_ulogic; + --shield_io28 : inout std_ulogic; + --shield_io29 : inout std_ulogic; shield_io30 : inout std_ulogic; shield_io31 : inout std_ulogic; shield_io32 : inout std_ulogic; @@ -762,24 +762,17 @@ begin gpio_in(16) <= sw2; gpio_in(17) <= sw3; - gpio_in(0) <= shield_io10; - gpio_in(1) <= shield_io11; - gpio_in(2) <= shield_io12; - gpio_in(3) <= shield_io13; - gpio_in(4) <= shield_io26; - gpio_in(5) <= shield_io27; - gpio_in(6) <= shield_io28; - gpio_in(7) <= shield_io29; - gpio_in(8) <= shield_io8; + gpio_in(0) <= '0'; + gpio_in(1) <= '0'; + gpio_in(2) <= '0'; + gpio_in(3) <= '0'; + gpio_in(4) <= '0'; + gpio_in(5) <= '0'; + gpio_in(6) <= '0'; + gpio_in(7) <= '0'; + gpio_in(8) <= '0'; + gpio_in(9) <= shield_io9; - --gpio_in(10) <= shield_io10; - --gpio_in(11) <= shield_io11; - --gpio_in(12) <= shield_io12; - --gpio_in(13) <= shield_io13; - --gpio_in(14) <= shield_io26; - --gpio_in(15) <= shield_io27; - --gpio_in(16) <= shield_io28; - --gpio_in(17) <= shield_io29; gpio_in(18) <= shield_io30; gpio_in(19) <= shield_io31; gpio_in(20) <= shield_io32; @@ -795,36 +788,19 @@ begin gpio_in(30) <= shield_io43; gpio_in(31) <= shield_io44; - led_b_pwm(1) <= gpio_out(0) when gpio_dir(0) = '1' else 'Z'; - led_g_pwm(1) <= gpio_out(1) when gpio_dir(1) = '1' else 'Z'; - led_r_pwm(1) <= gpio_out(2) when gpio_dir(2) = '1' else 'Z'; - - led_b_pwm(2) <= gpio_out(3) when gpio_dir(3) = '1' else 'Z'; - led_g_pwm(2) <= gpio_out(4) when gpio_dir(4) = '1' else 'Z'; - led_r_pwm(2) <= gpio_out(5) when gpio_dir(5) = '1' else 'Z'; - - led_b_pwm(3) <= gpio_out(6) when gpio_dir(6) = '1' else 'Z'; - led_g_pwm(3) <= gpio_out(7) when gpio_dir(7) = '1' else 'Z'; - led_r_pwm(3) <= gpio_out(8) when gpio_dir(8) = '1' else 'Z'; - - --shield_io0 <= gpio_out(0) when gpio_dir(0) = '1' else 'Z'; - --shield_io1 <= gpio_out(1) when gpio_dir(1) = '1' else 'Z'; - --shield_io2 <= gpio_out(2) when gpio_dir(2) = '1' else 'Z'; - --shield_io3 <= gpio_out(3) when gpio_dir(3) = '1' else 'Z'; - --shield_io4 <= gpio_out(4) when gpio_dir(4) = '1' else 'Z'; - --shield_io5 <= gpio_out(5) when gpio_dir(5) = '1' else 'Z'; - --shield_io6 <= gpio_out(6) when gpio_dir(6) = '1' else 'Z'; - --shield_io7 <= gpio_out(7) when gpio_dir(7) = '1' else 'Z'; - --shield_io8 <= gpio_out(8) when gpio_dir(8) = '1' else 'Z'; + led_b_pwm(1) <= gpio_out(0) and gpio_dir(0); + led_g_pwm(1) <= gpio_out(1) and gpio_dir(1); + led_r_pwm(1) <= gpio_out(2) and gpio_dir(2); + + led_b_pwm(2) <= gpio_out(3) and gpio_dir(3); + led_g_pwm(2) <= gpio_out(4) and gpio_dir(4); + led_r_pwm(2) <= gpio_out(5) and gpio_dir(5); + + led_b_pwm(3) <= gpio_out(6) and gpio_dir(6); + led_g_pwm(3) <= gpio_out(7) and gpio_dir(7); + led_r_pwm(3) <= gpio_out(8) and gpio_dir(8); + shield_io9 <= gpio_out(9) when gpio_dir(9) = '1' else 'Z'; - shield_io10 <= gpio_out(10) when gpio_dir(10) = '1' else 'Z'; - shield_io11 <= gpio_out(11) when gpio_dir(11) = '1' else 'Z'; - shield_io12 <= gpio_out(12) when gpio_dir(12) = '1' else 'Z'; - shield_io13 <= gpio_out(13) when gpio_dir(13) = '1' else 'Z'; - shield_io26 <= gpio_out(14) when gpio_dir(14) = '1' else 'Z'; - shield_io27 <= gpio_out(15) when gpio_dir(15) = '1' else 'Z'; - shield_io28 <= gpio_out(16) when gpio_dir(16) = '1' else 'Z'; - shield_io29 <= gpio_out(17) when gpio_dir(17) = '1' else 'Z'; shield_io30 <= gpio_out(18) when gpio_dir(18) = '1' else 'Z'; shield_io31 <= gpio_out(19) when gpio_dir(19) = '1' else 'Z'; shield_io32 <= gpio_out(20) when gpio_dir(20) = '1' else 'Z'; From 4f06a01731f9ebf5f3c49d93240a14b265a416f9 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 31 Dec 2025 08:43:44 +1100 Subject: [PATCH 02/10] arty a7: Add a second SD card interface on pmod JC This adds a second SD card interface. The main complexity is in providing a wishbone switch/arbiter to multiplex the two DMA wishbones from the two interfaces to a single wishbone going to the soc module. There is a new syscon info reg bit to indicate the presence of the second litesdcard. Signed-off-by: Paul Mackerras --- fpga/arty_a7.xdc | 16 +++++++ fpga/top-arty.vhdl | 93 +++++++++++++++++++++++++++++++++++------ include/microwatt_soc.h | 3 ++ soc.vhdl | 5 +++ syscon.vhdl | 5 +++ 5 files changed, 109 insertions(+), 13 deletions(-) diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index df55577..3fa99bb 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -133,6 +133,22 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard/sdpa #set_property -dict { PACKAGE_PIN T13 IOSTANDARD LVCMOS33 } [get_ports { pmod_jc_9 }]; #set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { pmod_jc_10 }]; +# connection to second Digilent PmodSD on JC +set_property -dict { PACKAGE_PIN U12 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard2_data[3] }]; +set_property -dict { PACKAGE_PIN V12 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard2_cmd }]; +set_property -dict { PACKAGE_PIN V10 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard2_data[0] }]; +set_property -dict { PACKAGE_PIN V11 IOSTANDARD LVCMOS33 SLEW FAST } [get_ports { sdcard2_clk }]; +set_property -dict { PACKAGE_PIN U14 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard2_data[1] }]; +set_property -dict { PACKAGE_PIN V14 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard2_data[2] }]; +set_property -dict { PACKAGE_PIN T13 IOSTANDARD LVCMOS33 } [get_ports { sdcard2_cd }]; +#set_property -dict { PACKAGE_PIN U13 IOSTANDARD LVCMOS33 } [get_ports { sdcard2_wp }]; + +# Put registers into IOBs to improve timing +set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/sdpads_data_i_reg*}] +set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/xilinxsdrtristateimpl*_o_reg}] +set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/sdcard_clk_reg}] +set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/sdpads_cmd_i_reg}] + ################################################################################ # PMOD header JD (standard, 200 ohm protection resisters) ################################################################################ diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index 54d1f90..43cea85 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -122,6 +122,12 @@ entity toplevel is sdcard_clk : out std_ulogic; sdcard_cd : in std_ulogic; + -- Second SD card + sdcard2_data : inout std_ulogic_vector(3 downto 0); + sdcard2_cmd : inout std_ulogic; + sdcard2_clk : out std_ulogic; + sdcard2_cd : in std_ulogic; + -- DRAM wires ddram_a : out std_ulogic_vector(13 downto 0); ddram_ba : out std_ulogic_vector(2 downto 0); @@ -179,11 +185,17 @@ architecture behaviour of toplevel is -- LiteSDCard connection signal ext_irq_sdcard : std_ulogic := '0'; + signal ext_irq_sdcard2 : std_ulogic := '0'; signal wb_sdcard_out : wb_io_slave_out := wb_io_slave_out_init; + signal wb_sdcard2_out : wb_io_slave_out := wb_io_slave_out_init; signal wb_sddma_out : wb_io_master_out := wb_io_master_out_init; signal wb_sddma_in : wb_io_slave_out; signal wb_sddma_nr : wb_io_master_out; + signal wb_sddma1_nr : wb_io_master_out; + signal wb_sddma2_nr : wb_io_master_out; signal wb_sddma_ir : wb_io_slave_out; + signal wb_sddma1_ack : std_ulogic; + signal wb_sddma2_ack : std_ulogic; -- for conversion from non-pipelined wishbone to pipelined signal wb_sddma_stb_sent : std_ulogic; @@ -261,6 +273,7 @@ begin UART0_IS_16550 => UART_IS_16550, HAS_UART1 => HAS_UART1, HAS_SD_CARD => USE_LITESDCARD, + HAS_SD_CARD2 => USE_LITESDCARD, HAS_GPIO => HAS_GPIO, NGPIO => NGPIO ) @@ -295,6 +308,7 @@ begin -- External interrupts ext_irq_eth => ext_irq_eth, ext_irq_sdcard => ext_irq_sdcard, + ext_irq_sdcard2 => ext_irq_sdcard2, -- DRAM wishbone wb_dram_in => wb_dram_in, @@ -623,7 +637,7 @@ begin ext_irq_eth <= '0'; end generate; - -- SD card pmod + -- SD card pmod, two interfaces has_sdcard : if USE_LITESDCARD generate component litesdcard_core port ( clk : in std_ulogic; @@ -662,7 +676,10 @@ begin end component; signal wb_sdcard_cyc : std_ulogic; + signal wb_sdcard2_cyc : std_ulogic; signal wb_sdcard_adr : std_ulogic_vector(29 downto 0); + signal dma_msel : std_ulogic; + signal other_cyc : std_ulogic; begin litesdcard : litesdcard_core @@ -680,14 +697,14 @@ begin wb_ctrl_cti => "000", wb_ctrl_bte => "00", wb_ctrl_err => open, - wb_dma_adr => wb_sddma_nr.adr, - wb_dma_dat_w => wb_sddma_nr.dat, + wb_dma_adr => wb_sddma1_nr.adr, + wb_dma_dat_w => wb_sddma1_nr.dat, wb_dma_dat_r => wb_sddma_ir.dat, - wb_dma_sel => wb_sddma_nr.sel, - wb_dma_cyc => wb_sddma_nr.cyc, - wb_dma_stb => wb_sddma_nr.stb, - wb_dma_ack => wb_sddma_ir.ack, - wb_dma_we => wb_sddma_nr.we, + wb_dma_sel => wb_sddma1_nr.sel, + wb_dma_cyc => wb_sddma1_nr.cyc, + wb_dma_stb => wb_sddma1_nr.stb, + wb_dma_ack => wb_sddma1_ack, + wb_dma_we => wb_sddma1_nr.we, wb_dma_cti => open, wb_dma_bte => open, wb_dma_err => '0', @@ -698,13 +715,55 @@ begin irq => ext_irq_sdcard ); - -- Gate cyc with chip select from SoC - wb_sdcard_cyc <= wb_ext_io_in.cyc and wb_ext_is_sdcard; + litesdcard2 : litesdcard_core + port map ( + clk => system_clk, + rst => periph_rst, + wb_ctrl_adr => wb_sdcard_adr, + wb_ctrl_dat_w => wb_ext_io_in.dat, + wb_ctrl_dat_r => wb_sdcard2_out.dat, + wb_ctrl_sel => wb_ext_io_in.sel, + wb_ctrl_cyc => wb_sdcard2_cyc, + wb_ctrl_stb => wb_ext_io_in.stb, + wb_ctrl_ack => wb_sdcard2_out.ack, + wb_ctrl_we => wb_ext_io_in.we, + wb_ctrl_cti => "000", + wb_ctrl_bte => "00", + wb_ctrl_err => open, + wb_dma_adr => wb_sddma2_nr.adr, + wb_dma_dat_w => wb_sddma2_nr.dat, + wb_dma_dat_r => wb_sddma_ir.dat, + wb_dma_sel => wb_sddma2_nr.sel, + wb_dma_cyc => wb_sddma2_nr.cyc, + wb_dma_stb => wb_sddma2_nr.stb, + wb_dma_ack => wb_sddma2_ack, + wb_dma_we => wb_sddma2_nr.we, + wb_dma_cti => open, + wb_dma_bte => open, + wb_dma_err => '0', + sdcard_data => sdcard2_data, + sdcard_cmd => sdcard2_cmd, + sdcard_clk => sdcard2_clk, + sdcard_cd => sdcard2_cd, + irq => ext_irq_sdcard2 + ); - wb_sdcard_adr <= x"0000" & wb_ext_io_in.adr(13 downto 0); + -- Gate cyc with chip selects from SoC + -- Select first or second interface based on real address bit 15 + wb_sdcard_cyc <= wb_ext_io_in.cyc and wb_ext_is_sdcard and not wb_ext_io_in.adr(13); + wb_sdcard2_cyc <= wb_ext_io_in.cyc and wb_ext_is_sdcard and wb_ext_io_in.adr(13); - wb_sdcard_out.stall <= not wb_sdcard_out.ack; + wb_sdcard_adr <= 17x"0" & wb_ext_io_in.adr(12 downto 0); + wb_sdcard_out.stall <= not wb_sdcard_out.ack; + wb_sdcard2_out.stall <= not wb_sdcard2_out.ack; + + -- Simple arbiter to multiplex the two DMA wishbones + wb_sddma_nr <= wb_sddma1_nr when dma_msel = '0' else wb_sddma2_nr; + wb_sddma1_ack <= wb_sddma_ir.ack and not dma_msel; + wb_sddma2_ack <= wb_sddma_ir.ack and dma_msel; + other_cyc <= wb_sddma2_nr.cyc when dma_msel = '0' else wb_sddma1_nr.cyc; + -- Convert non-pipelined DMA wishbone to pipelined by suppressing -- non-acknowledged strobes process(system_clk) @@ -721,6 +780,13 @@ begin wb_sddma_stb_sent <= wb_sddma_nr.stb; end if; wb_sddma_ir <= wb_sddma_in; + + -- Decide which wishbone to use next cycle + if periph_rst = '1' then + dma_msel <= '0'; + elsif wb_sddma_nr.cyc = '0' and other_cyc = '1' then + dma_msel <= not dma_msel; + end if; end if; end process; @@ -728,7 +794,8 @@ begin -- Mux WB response on the IO bus wb_ext_io_out <= wb_eth_out when wb_ext_is_eth = '1' else - wb_sdcard_out when wb_ext_is_sdcard = '1' else + wb_sdcard_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '0' else + wb_sdcard2_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '1' else wb_dram_ctrl_out; leds_pwm : process(system_clk) diff --git a/include/microwatt_soc.h b/include/microwatt_soc.h index 6e367b1..6203535 100644 --- a/include/microwatt_soc.h +++ b/include/microwatt_soc.h @@ -19,6 +19,7 @@ #define LETH_CSR_BASE 0xc8020000 /* LiteEth CSR registers */ #define LETH_SRAM_BASE 0xc8030000 /* LiteEth MMIO space */ #define LSDC_CSR_BASE 0xc8040000 /* LiteSDCard MMIO space */ +#define LSDC2_CSR_BASE 0xc8048000 /* 2nd LiteSDCard MMIO space */ #define SPI_FLASH_BASE 0xf0000000 /* SPI Flash memory map */ #define DRAM_INIT_BASE 0xff000000 /* Internal DRAM init firmware */ @@ -30,6 +31,7 @@ #define IRQ_UART1 2 #define IRQ_SDCARD 3 #define IRQ_GPIO 4 +#define IRQ_SDCARD2 5 /* * Register definitions for the syscon registers @@ -46,6 +48,7 @@ #define SYS_REG_INFO_HAS_UART1 (1ull << 6) #define SYS_REG_INFO_HAS_ARTB (1ull << 7) #define SYS_REG_INFO_HAS_LITESDCARD (1ull << 8) +#define SYS_REG_INFO_HAS_LITESDCARD2 (1ull << 9) #define SYS_REG_BRAMINFO 0x10 #define SYS_REG_BRAMINFO_SIZE_MASK 0xfffffffffffffull #define SYS_REG_DRAMINFO 0x18 diff --git a/soc.vhdl b/soc.vhdl index 3daeb73..fc30014 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -50,6 +50,7 @@ use work.wishbone_types.all; -- 2 : UART1 -- 3 : SD card -- 4 : GPIO +-- 5 : SD card 2 -- Resets: -- The soc can be reset externally by its parent top- entity (via rst port), @@ -93,6 +94,7 @@ entity soc is DCACHE_TLB_SET_SIZE : natural := 64; DCACHE_TLB_NUM_WAYS : natural := 2; HAS_SD_CARD : boolean := false; + HAS_SD_CARD2 : boolean := false; HAS_GPIO : boolean := false; NGPIO : natural := 32 ); @@ -122,6 +124,7 @@ entity soc is -- External interrupts ext_irq_eth : in std_ulogic := '0'; ext_irq_sdcard : in std_ulogic := '0'; + ext_irq_sdcard2 : in std_ulogic := '0'; -- UART0 signals: uart0_txd : out std_ulogic; @@ -822,6 +825,7 @@ begin SPI_FLASH_OFFSET => SPI_FLASH_OFFSET, HAS_LITEETH => HAS_LITEETH, HAS_SD_CARD => HAS_SD_CARD, + HAS_SD_CARD2 => HAS_SD_CARD2, UART0_IS_16550 => UART0_IS_16550, HAS_UART1 => HAS_UART1 ) @@ -1031,6 +1035,7 @@ begin int_level_in(2) <= uart1_irq; int_level_in(3) <= ext_irq_sdcard; int_level_in(4) <= gpio_intr; + int_level_in(5) <= ext_irq_sdcard2; end process; -- BRAM Memory slave diff --git a/syscon.vhdl b/syscon.vhdl index ad9ba2c..bdeabd2 100644 --- a/syscon.vhdl +++ b/syscon.vhdl @@ -21,6 +21,7 @@ entity syscon is SPI_FLASH_OFFSET : integer; HAS_LITEETH : boolean; HAS_SD_CARD : boolean; + HAS_SD_CARD2 : boolean; UART0_IS_16550 : boolean; HAS_UART1 : boolean ); @@ -75,6 +76,7 @@ architecture behaviour of syscon is constant SYS_REG_INFO_HAS_URT1 : integer := 6; -- Has second UART constant SYS_REG_INFO_HAS_ARTB : integer := 7; -- Has architected TB frequency constant SYS_REG_INFO_HAS_SDCARD : integer := 8; -- Has LiteSDCard SD-card interface + constant SYS_REG_INFO_HAS_SDCARD2 : integer := 9; -- Has 2nd LiteSDCard SD-card interface -- BRAMINFO contains the BRAM size in the bottom 52 bits -- DRAMINFO contains the DRAM size if any in the bottom 52 bits @@ -129,6 +131,7 @@ architecture behaviour of syscon is signal info_has_spif : std_ulogic; signal info_has_leth : std_ulogic; signal info_has_lsdc : std_ulogic; + signal info_has_lsd2 : std_ulogic; signal info_has_urt1 : std_ulogic; signal info_clk : std_ulogic_vector(39 downto 0); signal info_fl_off : std_ulogic_vector(31 downto 0); @@ -155,6 +158,7 @@ begin info_has_spif <= '1' when HAS_SPI_FLASH else '0'; info_has_leth <= '1' when HAS_LITEETH else '0'; info_has_lsdc <= '1' when HAS_SD_CARD else '0'; + info_has_lsd2 <= '1' when HAS_SD_CARD2 else '0'; info_has_urt1 <= '1' when HAS_UART1 else '0'; info_clk <= std_ulogic_vector(to_unsigned(CLK_FREQ, 40)); reg_info <= (SYS_REG_INFO_HAS_UART => info_has_uart, @@ -163,6 +167,7 @@ begin SYS_REG_INFO_HAS_SPIF => info_has_spif, SYS_REG_INFO_HAS_LETH => info_has_leth, SYS_REG_INFO_HAS_SDCARD => info_has_lsdc, + SYS_REG_INFO_HAS_SDCARD2 => info_has_lsd2, SYS_REG_INFO_HAS_LSYS => '1', SYS_REG_INFO_HAS_URT1 => info_has_urt1, others => '0'); From 90df07b9505fd4267e083089a43de5586f42a117 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 12 Sep 2023 08:51:36 +1000 Subject: [PATCH 03/10] arty a7: Add connection to i2c RTC chip on port JD The I2C data is on GPIO 22 and the clock is on GPIO 23. Signed-off-by: Paul Mackerras --- fpga/arty_a7.xdc | 8 ++++++-- fpga/top-arty.vhdl | 16 ++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index 3fa99bb..3bd7c9c 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -162,6 +162,10 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/sdp #set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 } [get_ports { pmod_jd_9 }]; #set_property -dict { PACKAGE_PIN G2 IOSTANDARD LVCMOS33 } [get_ports { pmod_jd_10 }]; +# connection to i2c RTC chip (Dallas DS3231) on JD +set_property -dict { PACKAGE_PIN D2 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { i2c_rtc_d }]; +set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { i2c_rtc_c }]; + ################################################################################ # Arduino/chipKIT shield connector ################################################################################ @@ -188,8 +192,8 @@ set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_po set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io31 }]; set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io32 }]; set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io33 }]; -set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io34 }]; -set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io35 }]; +#set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io34 }]; +#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io35 }]; set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io36 }]; set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io37 }]; set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io38 }]; diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index 43cea85..56060a2 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -90,8 +90,8 @@ entity toplevel is shield_io31 : inout std_ulogic; shield_io32 : inout std_ulogic; shield_io33 : inout std_ulogic; - shield_io34 : inout std_ulogic; - shield_io35 : inout std_ulogic; + --shield_io34 : inout std_ulogic; + --shield_io35 : inout std_ulogic; shield_io36 : inout std_ulogic; shield_io37 : inout std_ulogic; shield_io38 : inout std_ulogic; @@ -128,6 +128,10 @@ entity toplevel is sdcard2_clk : out std_ulogic; sdcard2_cd : in std_ulogic; + -- I2C RTC chip + i2c_rtc_d : inout std_ulogic; + i2c_rtc_c : inout std_ulogic; + -- DRAM wires ddram_a : out std_ulogic_vector(13 downto 0); ddram_ba : out std_ulogic_vector(2 downto 0); @@ -844,8 +848,8 @@ begin gpio_in(19) <= shield_io31; gpio_in(20) <= shield_io32; gpio_in(21) <= shield_io33; - gpio_in(22) <= shield_io34; - gpio_in(23) <= shield_io35; + gpio_in(22) <= i2c_rtc_d; + gpio_in(23) <= i2c_rtc_c; gpio_in(24) <= shield_io36; gpio_in(25) <= shield_io37; gpio_in(26) <= shield_io38; @@ -872,8 +876,8 @@ begin shield_io31 <= gpio_out(19) when gpio_dir(19) = '1' else 'Z'; shield_io32 <= gpio_out(20) when gpio_dir(20) = '1' else 'Z'; shield_io33 <= gpio_out(21) when gpio_dir(21) = '1' else 'Z'; - shield_io34 <= gpio_out(22) when gpio_dir(22) = '1' else 'Z'; - shield_io35 <= gpio_out(23) when gpio_dir(23) = '1' else 'Z'; + i2c_rtc_d <= gpio_out(22) when gpio_dir(22) = '1' else 'Z'; + i2c_rtc_c <= gpio_out(23) when gpio_dir(23) = '1' else 'Z'; shield_io36 <= gpio_out(24) when gpio_dir(24) = '1' else 'Z'; shield_io37 <= gpio_out(25) when gpio_dir(25) = '1' else 'Z'; shield_io38 <= gpio_out(26) when gpio_dir(26) = '1' else 'Z'; From 16c3eda1b1785b929f4e27a3e3c128c97b298e24 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 14 Jan 2026 14:43:35 +1100 Subject: [PATCH 04/10] arty a7: Rework status LED colours This frees up LEDs 4 and 5 by combining their status functions into LED 0, which is now black when the system is in reset and yellow when the system clock is not locked. On configuations without litedram, LED 0 now shows green rather than magenta. Signed-off-by: Paul Mackerras --- fpga/top-arty.vhdl | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index 56060a2..b824057 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -156,6 +156,8 @@ architecture behaviour of toplevel is -- Status signal run_out : std_ulogic; signal run_outs : std_ulogic_vector(CPUS-1 downto 0); + signal init_done : std_ulogic; + signal init_err : std_ulogic; -- Reset signals: signal soc_rst : std_ulogic; @@ -204,9 +206,9 @@ architecture behaviour of toplevel is signal wb_sddma_stb_sent : std_ulogic; -- Status LED - signal led_b_pwm : std_ulogic_vector(3 downto 0); - signal led_r_pwm : std_ulogic_vector(3 downto 0); - signal led_g_pwm : std_ulogic_vector(3 downto 0); + signal led_b_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); + signal led_r_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); + signal led_g_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); -- Dumb PWM for the LEDs, those RGB LEDs are too bright otherwise signal pwm_counter : std_ulogic_vector(8 downto 0); @@ -401,9 +403,8 @@ begin pll_locked_out => system_clk_locked ); - led_b_pwm <= "1111"; - led_r_pwm <= "1111"; - led_g_pwm <= "0000"; + init_done <= '1'; + init_err <= '0'; -- Vivado barfs on those differential signals if left -- unconnected. So instanciate a diff. buffer and feed @@ -501,9 +502,8 @@ begin ddram_reset_n => ddram_reset_n ); - led_b_pwm(0) <= not dram_init_done; - led_r_pwm(0) <= dram_init_error; - led_g_pwm(0) <= dram_init_done and not dram_init_error; + init_done <= dram_init_done; + init_err <= dram_init_error; end generate; @@ -802,6 +802,21 @@ begin wb_sdcard2_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '1' else wb_dram_ctrl_out; + status_led_colour : process(all) + variable rgb : std_ulogic_vector(2 downto 0); + begin + if soc_rst = '1' then + rgb := "000"; + elsif system_clk_locked = '0' then + rgb := "110"; + else + rgb := init_err & (init_done and not init_err) & (not init_done); + end if; + led_r_pwm(0) <= rgb(2); + led_g_pwm(0) <= rgb(1); + led_b_pwm(0) <= rgb(0); + end process; + leds_pwm : process(system_clk) begin if rising_edge(system_clk) then @@ -818,8 +833,8 @@ begin end if; end process; - led4 <= system_clk_locked; - led5 <= not soc_rst; + led4 <= '0'; + led5 <= '0'; led6 <= run_outs(1) when CPUS > 1 else '0'; led7 <= run_outs(0); From dcd1072c254daeb909e5d16b4792ade4736066e5 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 13 Jan 2024 20:46:55 +1100 Subject: [PATCH 05/10] arty a7: Put the top 8 GPIOs on pmod B Signed-off-by: Paul Mackerras --- fpga/arty_a7.xdc | 32 +++++++++++++------------- fpga/top-arty.vhdl | 56 ++++++++++++++++++++++++++-------------------- 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index 3bd7c9c..c52d072 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -101,14 +101,14 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard/sdpa # PMOD header JB (high-speed, no protection resisters) ################################################################################ -#set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_1 }]; -#set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_2 }]; -#set_property -dict { PACKAGE_PIN D15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_3 }]; -#set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_4 }]; -#set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_7 }]; -#set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_8 }]; -#set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_9 }]; -#set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_10 }]; +set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_1 }]; +set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_2 }]; +set_property -dict { PACKAGE_PIN D15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_3 }]; +set_property -dict { PACKAGE_PIN C15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_4 }]; +set_property -dict { PACKAGE_PIN J17 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_7 }]; +set_property -dict { PACKAGE_PIN J18 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_8 }]; +set_property -dict { PACKAGE_PIN K15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_9 }]; +set_property -dict { PACKAGE_PIN J15 IOSTANDARD LVCMOS33 } [get_ports { pmod_jb_10 }]; # connection to Digilent PmodSD on JB #set_property -dict { PACKAGE_PIN E15 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard_data[3] }]; @@ -194,15 +194,15 @@ set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_po set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io33 }]; #set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io34 }]; #set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io35 }]; -set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io36 }]; -set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io37 }]; -set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io38 }]; -set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io39 }]; -set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io40 }]; -set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io41 }]; +#set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io36 }]; +#set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io37 }]; +#set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io38 }]; +#set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io39 }]; +#set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io40 }]; +#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io41 }]; #set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io42 }]; # A -set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io43 }]; # SCL -set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io44 }]; # SDA +#set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io43 }]; # SCL +#set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { shield_io44 }]; # SDA #set_property -dict { PACKAGE_PIN C2 IOSTANDARD LVCMOS33 } [get_ports { shield_rst }]; #set_property -dict { PACKAGE_PIN C1 IOSTANDARD LVCMOS33 } [get_ports { spi_hdr_ss }]; diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index b824057..ac7d3c0 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -92,14 +92,22 @@ entity toplevel is shield_io33 : inout std_ulogic; --shield_io34 : inout std_ulogic; --shield_io35 : inout std_ulogic; - shield_io36 : inout std_ulogic; - shield_io37 : inout std_ulogic; - shield_io38 : inout std_ulogic; - shield_io39 : inout std_ulogic; - shield_io40 : inout std_ulogic; - shield_io41 : inout std_ulogic; - shield_io43 : inout std_ulogic; - shield_io44 : inout std_ulogic; + --shield_io36 : inout std_ulogic; + --shield_io37 : inout std_ulogic; + --shield_io38 : inout std_ulogic; + --shield_io39 : inout std_ulogic; + --shield_io40 : inout std_ulogic; + --shield_io41 : inout std_ulogic; + --shield_io43 : inout std_ulogic; + --shield_io44 : inout std_ulogic; + pmod_jb_1 : inout std_ulogic; + pmod_jb_2 : inout std_ulogic; + pmod_jb_3 : inout std_ulogic; + pmod_jb_4 : inout std_ulogic; + pmod_jb_7 : inout std_ulogic; + pmod_jb_8 : inout std_ulogic; + pmod_jb_9 : inout std_ulogic; + pmod_jb_10 : inout std_ulogic; -- Ethernet eth_ref_clk : out std_ulogic; @@ -865,14 +873,14 @@ begin gpio_in(21) <= shield_io33; gpio_in(22) <= i2c_rtc_d; gpio_in(23) <= i2c_rtc_c; - gpio_in(24) <= shield_io36; - gpio_in(25) <= shield_io37; - gpio_in(26) <= shield_io38; - gpio_in(27) <= shield_io39; - gpio_in(28) <= shield_io40; - gpio_in(29) <= shield_io41; - gpio_in(30) <= shield_io43; - gpio_in(31) <= shield_io44; + gpio_in(24) <= pmod_jb_1; + gpio_in(25) <= pmod_jb_2; + gpio_in(26) <= pmod_jb_3; + gpio_in(27) <= pmod_jb_4; + gpio_in(28) <= pmod_jb_7; + gpio_in(29) <= pmod_jb_8; + gpio_in(30) <= pmod_jb_9; + gpio_in(31) <= pmod_jb_10; led_b_pwm(1) <= gpio_out(0) and gpio_dir(0); led_g_pwm(1) <= gpio_out(1) and gpio_dir(1); @@ -893,13 +901,13 @@ begin shield_io33 <= gpio_out(21) when gpio_dir(21) = '1' else 'Z'; i2c_rtc_d <= gpio_out(22) when gpio_dir(22) = '1' else 'Z'; i2c_rtc_c <= gpio_out(23) when gpio_dir(23) = '1' else 'Z'; - shield_io36 <= gpio_out(24) when gpio_dir(24) = '1' else 'Z'; - shield_io37 <= gpio_out(25) when gpio_dir(25) = '1' else 'Z'; - shield_io38 <= gpio_out(26) when gpio_dir(26) = '1' else 'Z'; - shield_io39 <= gpio_out(27) when gpio_dir(27) = '1' else 'Z'; - shield_io40 <= gpio_out(28) when gpio_dir(28) = '1' else 'Z'; - shield_io41 <= gpio_out(29) when gpio_dir(29) = '1' else 'Z'; - shield_io43 <= gpio_out(30) when gpio_dir(30) = '1' else 'Z'; - shield_io44 <= gpio_out(31) when gpio_dir(31) = '1' else 'Z'; + pmod_jb_1 <= gpio_out(24) when gpio_dir(24) = '1' else 'Z'; + pmod_jb_2 <= gpio_out(25) when gpio_dir(25) = '1' else 'Z'; + pmod_jb_3 <= gpio_out(26) when gpio_dir(26) = '1' else 'Z'; + pmod_jb_4 <= gpio_out(27) when gpio_dir(27) = '1' else 'Z'; + pmod_jb_7 <= gpio_out(28) when gpio_dir(28) = '1' else 'Z'; + pmod_jb_8 <= gpio_out(29) when gpio_dir(29) = '1' else 'Z'; + pmod_jb_9 <= gpio_out(30) when gpio_dir(30) = '1' else 'Z'; + pmod_jb_10 <= gpio_out(31) when gpio_dir(31) = '1' else 'Z'; end architecture behaviour; From d4fec95044e6259fe6cf1f33c2f47edcb43fbf4d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 28 Jan 2026 10:27:24 +1100 Subject: [PATCH 06/10] arty a7: Turn on LED 5 when SD card command-done interrupt is enabled This snoops writes to the interrupt enable registers of the SD card interfaces and records whether the command-done interrupt is enabled. LED 5 is turned on whenever either interface has this interrupt enabled in order to serve as a disk activity indicator. Signed-off-by: Paul Mackerras --- fpga/top-arty.vhdl | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index ac7d3c0..b4ba15a 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -217,6 +217,7 @@ architecture behaviour of toplevel is signal led_b_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); signal led_r_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); signal led_g_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); + signal disk_activity : std_ulogic := '0'; -- Dumb PWM for the LEDs, those RGB LEDs are too bright otherwise signal pwm_counter : std_ulogic_vector(8 downto 0); @@ -692,6 +693,8 @@ begin signal wb_sdcard_adr : std_ulogic_vector(29 downto 0); signal dma_msel : std_ulogic; signal other_cyc : std_ulogic; + signal sdc0_activity : std_ulogic := '0'; + signal sdc1_activity : std_ulogic := '0'; begin litesdcard : litesdcard_core @@ -802,6 +805,27 @@ begin end if; end process; + -- Capture writes to the interrupt enable registers, and record + -- the state of the command-done interrupt enable bit to use + -- as an activity indicator. + process(system_clk) + begin + if rising_edge(system_clk) then + if periph_rst = '1' then + sdc0_activity <= '0'; + sdc1_activity <= '0'; + elsif wb_sdcard_adr(11 downto 0) = x"602" + and wb_ext_io_in.stb = '1' and wb_ext_io_in.we = '1' then + if wb_sdcard_cyc = '1' then + sdc0_activity <= wb_ext_io_in.dat(3); + end if; + if wb_sdcard2_cyc = '1' then + sdc1_activity <= wb_ext_io_in.dat(3); + end if; + end if; + end if; + end process; + disk_activity <= sdc0_activity or sdc1_activity; end generate; -- Mux WB response on the IO bus @@ -842,7 +866,7 @@ begin end process; led4 <= '0'; - led5 <= '0'; + led5 <= disk_activity; led6 <= run_outs(1) when CPUS > 1 else '0'; led7 <= run_outs(0); From 7f4e0185b54d347b9c50bf027c44fbc2a973da1c Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 27 Jan 2026 15:44:44 +1100 Subject: [PATCH 07/10] xilinx_mult: Eliminate a Vivado warning Since the p1 instance of DSP48 has CREG = 0, we should ground the CEC input, as mentioned in a Vivado warning. Signed-off-by: Paul Mackerras --- xilinx-mult.vhdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xilinx-mult.vhdl b/xilinx-mult.vhdl index 26ba5d7..b78e950 100644 --- a/xilinx-mult.vhdl +++ b/xilinx-mult.vhdl @@ -960,7 +960,7 @@ begin CEALUMODE => valid_1, CEB1 => '0', CEB2 => valid_1, - CEC => valid_1, + CEC => '0', CECARRYIN => '0', CECTRL => '0', CED => '0', From 172eae61cba4a11037f10e53a0f3f40ebdc7844a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 15 Dec 2021 19:59:15 +1100 Subject: [PATCH 08/10] arty a7: Add an interface for a TFT LCD touchscreen This adds an interface for an Arduino-compatible LCD touchscreen. The screen module plugs directly on to the Arduino/chipKit shield connector on the Arty A7. Unfortunately, the slightly strange way the resistive touchscreen is brought out (connected to the D0, D1, RS and CS pins) combined with the 200 ohm protection resisters on the Arty board mean that some hardware hacks to the module are necessary. I rewired mine so that D0 and D1 are on the A4 and A5 pins and the reset is where D0 was (shield I/O 8). This interface is suitable for boards with a HX8347 driver chip. The timing may not be quite suitable for other driver chips. The interface is a byte which can be read and written at 0xc8050000, containing an index register, and a 1-8 byte data register at 0xc8050008. Reading at offsets 1 to 7 from those addresses yields the same value as at offset 0. Writing 64 bits to the data register writes the bytes at offset 1, 0, 3, 2, 5, 4, 7, 6 in that order to the driver chip. This allows pixel data to be transferred using 64-bit writes, ending up in the frame buffer in the expected order (for 16-bit pixels, the driver chip expects MS byte then LS byte). 32-bit writes do 1, 0, 3, 2, and 16-bit writes do 1, 0. The touchscreen support so far is a 1-byte register containing bits to set RS, D0, D1 and CS high or low or make them tri-state. There is nothing to do analog conversions of the signal levels at this stage. Signed-off-by: Paul Mackerras --- fpga/arty-lcd-ts.vhdl | 214 ++++++++++++++++++++++++++++++++++++++++++ fpga/arty_a7.xdc | 20 ++++ fpga/top-arty.vhdl | 59 ++++++++++++ microwatt.core | 15 ++- soc.vhdl | 8 ++ 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 fpga/arty-lcd-ts.vhdl diff --git a/fpga/arty-lcd-ts.vhdl b/fpga/arty-lcd-ts.vhdl new file mode 100644 index 0000000..614bfa8 --- /dev/null +++ b/fpga/arty-lcd-ts.vhdl @@ -0,0 +1,214 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; +use work.wishbone_types.all; + +-- Interface for LCD/touchscreen connected to Arduino-compatible socket on Arty A7 +entity lcd_touchscreen is + port ( + clk : in std_ulogic; + rst : in std_ulogic; + wb_in : in wb_io_master_out; + wb_out : out wb_io_slave_out; + wb_sel : in std_ulogic; + tp : out std_ulogic; + + lcd_din : in std_ulogic_vector(7 downto 0); + lcd_dout : out std_ulogic_vector(7 downto 0); + lcd_doe : out std_ulogic; + lcd_doe0 : out std_ulogic; + lcd_doe1 : out std_ulogic; + lcd_rd : out std_ulogic; -- note active low + lcd_wr : out std_ulogic; -- note active low + lcd_rs : out std_ulogic; + lcd_rsoe : out std_ulogic; + lcd_cs : out std_ulogic; -- note active low + lcd_csoe : out std_ulogic; + lcd_rst : out std_ulogic -- note active low + ); +end entity lcd_touchscreen; + +architecture rtl of lcd_touchscreen is + + type state_t is (idle, prep1, prep2, writing, wr_pause, reading, rd_recovery); + + signal state : state_t; + signal delay : unsigned(5 downto 0); + signal ack : std_ulogic; + signal idle1 : std_ulogic; + signal idle2 : std_ulogic; + + signal rs : std_ulogic; + signal rsoe : std_ulogic; + signal cs : std_ulogic; + signal csoe : std_ulogic; + signal d0 : std_ulogic; + signal doe0 : std_ulogic; + signal doe1 : std_ulogic; + signal d1 : std_ulogic; + signal tsctrl : std_ulogic; + + signal wr_data : std_ulogic_vector(31 downto 0); + signal rd_data : std_ulogic_vector(7 downto 0); + signal wr_sel : std_ulogic_vector(3 downto 0); + signal req_wr : std_ulogic; + + -- Assume touchscreen is connected as follows: + -- X+ -> A5 (D1), X- -> A3 (CS), Y+ -> A2 (RS), Y- -> A4 (D0) + +begin + -- for now; should make sure it is at least 10us wide + lcd_rst <= not rst; + + wb_out.dat <= rd_data & rd_data & rd_data & rd_data; + wb_out.ack <= ack; + wb_out.stall <= '0' when state = idle else '1'; + + lcd_doe0 <= doe0; + lcd_doe1 <= doe1; + lcd_rs <= rs; + lcd_rsoe <= rsoe; + lcd_cs <= cs; + lcd_csoe <= csoe; + + tp <= tsctrl; + + process (clk) + begin + if rising_edge(clk) then + ack <= '0'; + idle2 <= idle1; + if rst = '1' then + state <= idle; + delay <= to_unsigned(0, 6); + rd_data <= (others => '0'); + lcd_rd <= '1'; + lcd_wr <= '1'; + cs <= '1'; + csoe <= '1'; + rs <= '0'; + rsoe <= '1'; + lcd_doe <= '0'; + doe0 <= '0'; + doe1 <= '0'; + d0 <= '0'; + d1 <= '0'; + idle1 <= '0'; + idle2 <= '0'; + tsctrl <= '0'; + elsif delay /= "000000" then + delay <= delay - 1; + else + case state is + when idle => + req_wr <= wb_in.we; + wr_data <= wb_in.dat; + wr_sel <= wb_in.sel; + if idle2 = '1' then + -- delay this one cycle after entering idle + lcd_doe <= '0'; + doe0 <= '0'; + doe1 <= '0'; + end if; + idle1 <= '0'; + if wb_in.cyc = '1' and wb_in.stb = '1' and wb_sel = '1' then + if wb_in.sel = "0000" then + ack <= '1'; + else + if wb_in.we = '1' or wb_in.adr(2) = '1' then + ack <= '1'; + end if; + if wb_in.adr(2) = '0' then + tsctrl <= '0'; + csoe <= '1'; + cs <= '0'; -- active low + rsoe <= '1'; + rs <= wb_in.adr(1); + doe0 <= '0'; + doe1 <= '0'; + state <= prep1; + else + tsctrl <= '1'; + idle2 <= '0'; + rd_data <= rsoe & rs & doe0 & d0 & doe1 & d1 & csoe & cs; + if wb_in.we = '1' and wb_in.sel(0) = '1' then + rsoe <= wb_in.dat(7); + rs <= wb_in.dat(6); + doe0 <= wb_in.dat(5); + d0 <= wb_in.dat(4); + lcd_dout(0) <= wb_in.dat(4); + doe1 <= wb_in.dat(3); + d1 <= wb_in.dat(2); + lcd_dout(1) <= wb_in.dat(2); + csoe <= wb_in.dat(1); + cs <= wb_in.dat(0); + end if; + end if; + end if; + else + if tsctrl = '0' then + cs <= '1'; + end if; + end if; + when prep1 => + lcd_doe <= req_wr; + doe0 <= req_wr; + doe1 <= req_wr; + if req_wr = '1' then + if wr_sel(1 downto 0) /= "00" then + if wr_sel(1) = '1' then + lcd_dout <= wr_data(15 downto 8); + wr_sel(1) <= '0'; + else + lcd_dout <= wr_data(7 downto 0); + wr_sel(0) <= '0'; + end if; + else + if wr_sel(3) = '1' then + lcd_dout <= wr_data(31 downto 24); + wr_sel(3) <= '0'; + else + lcd_dout <= wr_data(23 downto 16); + wr_sel(2) <= '0'; + end if; + end if; + end if; + state <= prep2; + when prep2 => + if req_wr = '1' then + lcd_wr <= '0'; -- active low + state <= writing; + delay <= to_unsigned(1, 6); + else + lcd_rd <= '0'; + state <= reading; + delay <= to_unsigned(35, 6); + end if; + when writing => + -- last cycle of writing state + lcd_wr <= '1'; + if wr_sel = "0000" then + state <= idle; + idle1 <= '1'; + else + state <= wr_pause; + end if; + when wr_pause => + state <= prep1; + when reading => + -- last cycle of reading state + lcd_rd <= '1'; + rd_data <= lcd_din; + ack <= '1'; + state <= rd_recovery; + delay <= to_unsigned(6, 6); + when rd_recovery => + state <= idle; + end case; + end if; + end if; + end process; + +end architecture; diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index c52d072..7f6a0a5 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -166,6 +166,26 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard2/sdp set_property -dict { PACKAGE_PIN D2 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { i2c_rtc_d }]; set_property -dict { PACKAGE_PIN H2 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { i2c_rtc_c }]; +################################################################################ +# TFT LCD shield (arduino-compatible) +# hacked to swap the LCD_RST and LCD_D0 lines, and put LCD_D1 on A5 +################################################################################ + +set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 PULLDOWN TRUE } [get_ports { lcd_rst }]; +#set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { lcd_tp }]; +set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[2] }]; +set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[3] }]; +set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[4] }]; +set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[5] }]; +set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[6] }]; +set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[7] }]; +set_property -dict { PACKAGE_PIN F5 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { lcd_rd }]; # A0 +set_property -dict { PACKAGE_PIN D8 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { lcd_wr }]; # A1 +set_property -dict { PACKAGE_PIN C7 IOSTANDARD LVCMOS33 } [get_ports { lcd_rs }]; # A2 +set_property -dict { PACKAGE_PIN E7 IOSTANDARD LVCMOS33 } [get_ports { lcd_cs }]; # A3 +set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[0] }]; # A4 +set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[1] }]; # A5 + ################################################################################ # Arduino/chipKIT shield connector ################################################################################ diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index b4ba15a..cf5f775 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -30,6 +30,7 @@ entity toplevel is HAS_UART1 : boolean := true; USE_LITESDCARD : boolean := false; HAS_GPIO : boolean := true; + USE_LCD : boolean := true; NGPIO : natural := 32 ); port( @@ -140,6 +141,14 @@ entity toplevel is i2c_rtc_d : inout std_ulogic; i2c_rtc_c : inout std_ulogic; + -- LCD display interface + lcd_d : inout std_ulogic_vector(7 downto 0); + lcd_rs : out std_ulogic; + lcd_cs : out std_ulogic; + lcd_rd : out std_ulogic; + lcd_wr : out std_ulogic; + lcd_rst : out std_ulogic; + -- DRAM wires ddram_a : out std_ulogic_vector(13 downto 0); ddram_ba : out std_ulogic_vector(2 downto 0); @@ -185,6 +194,7 @@ architecture behaviour of toplevel is signal wb_ext_is_dram_init : std_ulogic; signal wb_ext_is_eth : std_ulogic; signal wb_ext_is_sdcard : std_ulogic; + signal wb_ext_is_lcd : std_ulogic; -- DRAM main data wishbone connection signal wb_dram_in : wishbone_master_out; @@ -213,6 +223,9 @@ architecture behaviour of toplevel is -- for conversion from non-pipelined wishbone to pipelined signal wb_sddma_stb_sent : std_ulogic; + -- LCD touchscreen connection + signal wb_lcd_out : wb_io_slave_out := wb_io_slave_out_init; + -- Status LED signal led_b_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); signal led_r_pwm : std_ulogic_vector(3 downto 0) := (others => '0'); @@ -289,6 +302,7 @@ begin HAS_UART1 => HAS_UART1, HAS_SD_CARD => USE_LITESDCARD, HAS_SD_CARD2 => USE_LITESDCARD, + HAS_LCD => USE_LCD, HAS_GPIO => HAS_GPIO, NGPIO => NGPIO ) @@ -336,6 +350,7 @@ begin wb_ext_is_dram_init => wb_ext_is_dram_init, wb_ext_is_eth => wb_ext_is_eth, wb_ext_is_sdcard => wb_ext_is_sdcard, + wb_ext_is_lcd => wb_ext_is_lcd, -- DMA wishbone wishbone_dma_in => wb_sddma_in, @@ -828,10 +843,54 @@ begin disk_activity <= sdc0_activity or sdc1_activity; end generate; + -- LCD touchscreen on arduino-compatible pins + has_lcd : if USE_LCD generate + signal lcd_dout : std_ulogic_vector(7 downto 0); + signal lcd_doe : std_ulogic; + signal lcd_doe0 : std_ulogic; + signal lcd_doe1 : std_ulogic; + signal lcd_rso : std_ulogic; + signal lcd_rsoe : std_ulogic; + signal lcd_cso : std_ulogic; + signal lcd_csoe : std_ulogic; + signal tp : std_ulogic; + begin + lcd0 : entity work.lcd_touchscreen + port map ( + clk => system_clk, + rst => soc_rst, + wb_in => wb_ext_io_in, + wb_out => wb_lcd_out, + wb_sel => wb_ext_is_lcd, + tp => tp, + + lcd_din => lcd_d, + lcd_dout => lcd_dout, + lcd_doe => lcd_doe, + lcd_doe0 => lcd_doe0, + lcd_doe1 => lcd_doe1, + lcd_rd => lcd_rd, + lcd_wr => lcd_wr, + lcd_rs => lcd_rso, + lcd_rsoe => lcd_rsoe, + lcd_cs => lcd_cso, + lcd_csoe => lcd_csoe, + lcd_rst => lcd_rst + ); + -- lcd_d(0), lcd_d(1), lcd_rs, lcd_cs are used for the touchscreen + -- interface and hence have individual output enables. + lcd_d(0) <= lcd_dout(0) when lcd_doe0 = '1' else 'Z'; + lcd_d(1) <= lcd_dout(1) when lcd_doe1 = '1' else 'Z'; + lcd_d(7 downto 2) <= lcd_dout(7 downto 2) when lcd_doe = '1' else (others => 'Z'); + lcd_rs <= lcd_rso when lcd_rsoe = '1' else 'Z'; + lcd_cs <= lcd_cso when lcd_csoe = '1' else 'Z'; + end generate; + -- Mux WB response on the IO bus wb_ext_io_out <= wb_eth_out when wb_ext_is_eth = '1' else wb_sdcard_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '0' else wb_sdcard2_out when wb_ext_is_sdcard = '1' and wb_ext_io_in.adr(13) = '1' else + wb_lcd_out when wb_ext_is_lcd = '1' else wb_dram_ctrl_out; status_led_colour : process(all) diff --git a/microwatt.core b/microwatt.core index c1f6184..b54248e 100644 --- a/microwatt.core +++ b/microwatt.core @@ -139,6 +139,10 @@ filesets: uart16550: depend : [":microwatt:uart16550"] + lcdts: + files: + - fpga/arty-lcd-ts.vhdl : {file_type : vhdlSource-2008} + targets: nexys_a7: default_tool: vivado @@ -315,7 +319,7 @@ targets: arty_a7-100-nodram: default_tool: vivado - filesets: [core, arty_a7, soc, fpga, debug_xilinx, uart16550, xilinx_specific, litesdcard] + filesets: [core, arty_a7, soc, fpga, debug_xilinx, uart16550, xilinx_specific, litesdcard, lcdts] parameters : - memory_size - ram_init_file @@ -336,7 +340,7 @@ targets: arty_a7-100: default_tool: vivado - filesets: [core, arty_a7, soc, fpga, debug_xilinx, litedram, liteeth, uart16550, xilinx_specific, litesdcard] + filesets: [core, arty_a7, soc, fpga, debug_xilinx, litedram, liteeth, uart16550, xilinx_specific, litesdcard, lcdts] parameters: - cpus - memory_size @@ -344,6 +348,7 @@ targets: - use_litedram=true - use_liteeth=true - use_litesdcard + - use_lcd - disable_flatten_core - no_bram - spi_flash_offset=4194304 @@ -570,6 +575,12 @@ parameters: paramtype : generic default : false + use_lcd: + datatype : bool + description : Use LCD touchscreen interface + paramtype : generic + default : false + uart_is_16550: datatype : bool description : Use 16550-compatible UART from OpenCores diff --git a/soc.vhdl b/soc.vhdl index fc30014..539b26e 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -34,6 +34,7 @@ use work.wishbone_types.all; -- 0xc8020000: LiteEth CSRs (*) -- 0xc8030000: LiteEth MMIO (*) -- 0xc8040000: LiteSDCard CSRs +-- 0xc8050000: LCD touchscreen interface -- (*) LiteEth must be a single aligned 32KB block as the CSRs and MMIOs -- are actually decoded as a single wishbone which LiteEth will @@ -95,6 +96,7 @@ entity soc is DCACHE_TLB_NUM_WAYS : natural := 2; HAS_SD_CARD : boolean := false; HAS_SD_CARD2 : boolean := false; + HAS_LCD : boolean := false; HAS_GPIO : boolean := false; NGPIO : natural := 32 ); @@ -116,6 +118,7 @@ entity soc is wb_ext_is_dram_init : out std_ulogic; wb_ext_is_eth : out std_ulogic; wb_ext_is_sdcard : out std_ulogic; + wb_ext_is_lcd : out std_ulogic; -- external DMA wishbone with 32-bit data/address wishbone_dma_in : out wb_io_slave_out := wb_io_slave_out_init; @@ -686,6 +689,7 @@ begin wb_ext_is_dram_csr <= '0'; wb_ext_is_eth <= '0'; wb_ext_is_sdcard <= '0'; + wb_ext_is_lcd <= '0'; end if; if do_cyc = '1' then -- Decode I/O address @@ -715,6 +719,10 @@ begin slave_io := SLAVE_IO_EXTERNAL; io_cycle_external <= '1'; wb_ext_is_sdcard <= '1'; + elsif std_match(match, x"--05-") and HAS_LCD then + slave_io := SLAVE_IO_EXTERNAL; + io_cycle_external <= '1'; + wb_ext_is_lcd <= '1'; else io_cycle_none <= '1'; end if; From c7531e592c06f455cd5de65c2c51a05dd72dbc2c Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 18 Dec 2021 16:27:11 +1100 Subject: [PATCH 09/10] arty a7: Add facilities to get A/D conversions from the touchscreen This adds connections from the A2 - A5 inputs on the Arty A7 to the XADC module in the Artix-7 plus a way for software to access the XADC via its DRP port, and a status register to tell software when conversion sequences are done. Signed-off-by: Paul Mackerras --- fpga/arty-lcd-ts.vhdl | 137 +++++++++++++++++++++++++++++++++++++++--- fpga/arty_a7.xdc | 12 ++++ fpga/top-arty.vhdl | 21 ++++++- 3 files changed, 159 insertions(+), 11 deletions(-) diff --git a/fpga/arty-lcd-ts.vhdl b/fpga/arty-lcd-ts.vhdl index 614bfa8..2ec3786 100644 --- a/fpga/arty-lcd-ts.vhdl +++ b/fpga/arty-lcd-ts.vhdl @@ -2,6 +2,9 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; +library unisim; +use unisim.vcomponents.all; + library work; use work.wishbone_types.all; @@ -26,13 +29,24 @@ entity lcd_touchscreen is lcd_rsoe : out std_ulogic; lcd_cs : out std_ulogic; -- note active low lcd_csoe : out std_ulogic; - lcd_rst : out std_ulogic -- note active low + lcd_rst : out std_ulogic; -- note active low + + -- Differential analog inputs from touchscreen + a2_p : in std_ulogic; + a2_n : in std_ulogic; + a3_p : in std_ulogic; + a3_n : in std_ulogic; + a4_p : in std_ulogic; + a4_n : in std_ulogic; + a5_p : in std_ulogic; + a5_n : in std_ulogic ); end entity lcd_touchscreen; architecture rtl of lcd_touchscreen is - type state_t is (idle, prep1, prep2, writing, wr_pause, reading, rd_recovery); + type state_t is (idle, prep1, prep2, writing, wr_pause, reading, rd_recovery, + drp_lo, drp_hi); signal state : state_t; signal delay : unsigned(5 downto 0); @@ -51,18 +65,62 @@ architecture rtl of lcd_touchscreen is signal tsctrl : std_ulogic; signal wr_data : std_ulogic_vector(31 downto 0); - signal rd_data : std_ulogic_vector(7 downto 0); + signal rd_data : std_ulogic_vector(31 downto 0); signal wr_sel : std_ulogic_vector(3 downto 0); signal req_wr : std_ulogic; - -- Assume touchscreen is connected as follows: - -- X+ -> A5 (D1), X- -> A3 (CS), Y+ -> A2 (RS), Y- -> A4 (D0) + signal xadc_di : std_ulogic_vector(15 downto 0); + signal xadc_do : std_ulogic_vector(15 downto 0); + signal xadc_addr : std_ulogic_vector(6 downto 0); + signal xadc_en : std_ulogic; + signal xadc_we : std_ulogic; + signal xadc_rdy : std_ulogic; + signal xadc_eoc : std_ulogic; + signal xadc_eos : std_ulogic; + signal xadc_busy : std_ulogic; + signal eoc_stat : std_ulogic; + signal eos_stat : std_ulogic; + + -- Assume touchscreen is connected to the A2 - A5 analog inputs begin + + -- The connection of the analog A0 - A5 pins on the Arty + -- to FPGA pins is as follows: + -- A0 connects to AD4P/AD4N + -- A1 connects to AD5P/AD5N + -- A2 connects to AD6P/AD6N + -- A3 connects to AD7P/AD7N + -- A4 connects to AD15P/AD15N + -- A5 connects to AD0P/AD0N + xadc_0 : XADC + generic map ( + init_42 => x"0400" -- adcclk = dclk / 4, i.e. 25MHz + ) + port map ( + di => xadc_di, + do => xadc_do, + daddr => xadc_addr, + den => xadc_en, + dwe => xadc_we, + dclk => clk, + drdy => xadc_rdy, + reset => rst, + convst => '0', + convstclk => '0', + vp => '0', + vn => '0', + vauxp => a4_p & "0000000" & a3_p & a2_p & "00000" & a5_p, + vauxn => a4_n & "0000000" & a3_n & a2_n & "00000" & a5_n, + eoc => xadc_eoc, + eos => xadc_eos, + busy => xadc_busy + ); + -- for now; should make sure it is at least 10us wide lcd_rst <= not rst; - wb_out.dat <= rd_data & rd_data & rd_data & rd_data; + wb_out.dat <= rd_data; wb_out.ack <= ack; wb_out.stall <= '0' when state = idle else '1'; @@ -76,10 +134,18 @@ begin tp <= tsctrl; process (clk) + variable rdat : std_ulogic_vector(7 downto 0); begin if rising_edge(clk) then ack <= '0'; + xadc_en <= '0'; idle2 <= idle1; + if xadc_eoc = '1' then + eoc_stat <= '1'; + end if; + if xadc_eos = '1' then + eos_stat <= '1'; + end if; if rst = '1' then state <= idle; delay <= to_unsigned(0, 6); @@ -98,6 +164,9 @@ begin idle1 <= '0'; idle2 <= '0'; tsctrl <= '0'; + xadc_en <= '0'; + eoc_stat <= '0'; + eos_stat <= '0'; elsif delay /= "000000" then delay <= delay - 1; else @@ -116,11 +185,12 @@ begin if wb_in.cyc = '1' and wb_in.stb = '1' and wb_sel = '1' then if wb_in.sel = "0000" then ack <= '1'; - else + elsif wb_in.adr(6) = '0' then if wb_in.we = '1' or wb_in.adr(2) = '1' then ack <= '1'; end if; if wb_in.adr(2) = '0' then + -- c8050000 or 8, access LCD controller chip tsctrl <= '0'; csoe <= '1'; cs <= '0'; -- active low @@ -129,10 +199,12 @@ begin doe0 <= '0'; doe1 <= '0'; state <= prep1; - else + elsif wb_in.adr(1) = '0' then + -- c8050010, touchscreen drive register tsctrl <= '1'; idle2 <= '0'; - rd_data <= rsoe & rs & doe0 & d0 & doe1 & d1 & csoe & cs; + rdat := rsoe & rs & doe0 & d0 & doe1 & d1 & csoe & cs; + rd_data <= rdat & rdat & rdat & rdat; if wb_in.we = '1' and wb_in.sel(0) = '1' then rsoe <= wb_in.dat(7); rs <= wb_in.dat(6); @@ -145,6 +217,33 @@ begin csoe <= wb_in.dat(1); cs <= wb_in.dat(0); end if; + else + -- c8050018, touchscreen status register + rdat := 4x"0" & xadc_busy & eoc_stat & eos_stat & tsctrl; + rd_data <= rdat & rdat & rdat & rdat; + if wb_in.we = '1' and wb_in.sel(0) = '1' then + -- for eoc_stat and eos_state, write 0 to clear + if wb_in.dat(2) = '0' then + eoc_stat <= '0'; + end if; + if wb_in.dat(1) = '0' then + eos_stat <= '0'; + end if; + end if; + end if; + else + -- c80501xx, access to the XADC DRP port + xadc_en <= '1'; + xadc_we <= wb_in.we; + xadc_addr(6 downto 1) <= wb_in.adr(5 downto 0); + if wb_in.sel(1 downto 0) = "00" then + xadc_di <= wb_in.dat(31 downto 16); + xadc_addr(0) <= '1'; + state <= drp_hi; + else + xadc_di <= wb_in.dat(15 downto 0); + xadc_addr(0) <= '0'; + state <= drp_lo; end if; end if; else @@ -200,12 +299,30 @@ begin when reading => -- last cycle of reading state lcd_rd <= '1'; - rd_data <= lcd_din; + rd_data <= lcd_din & lcd_din & lcd_din & lcd_din; ack <= '1'; state <= rd_recovery; delay <= to_unsigned(6, 6); when rd_recovery => state <= idle; + when drp_lo => + if xadc_rdy = '1' then + rd_data(15 downto 0) <= xadc_do; + if wr_sel(3 downto 2) = "00" then + ack <= '1'; + state <= idle; + else + xadc_di <= wr_data(31 downto 16); + xadc_addr(0) <= '1'; + state <= drp_hi; + end if; + end if; + when drp_hi => + if xadc_rdy = '1' then + rd_data(31 downto 16) <= xadc_do; + ack <= '1'; + state <= idle; + end if; end case; end if; end if; diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index 7f6a0a5..6490f5d 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -186,6 +186,18 @@ set_property -dict { PACKAGE_PIN E7 IOSTANDARD LVCMOS33 } [get_ports { lcd_cs } set_property -dict { PACKAGE_PIN D7 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[0] }]; # A4 set_property -dict { PACKAGE_PIN D5 IOSTANDARD LVCMOS33 } [get_ports { lcd_d[1] }]; # A5 +################################################################################ +# Analog inputs (connected to touchscreen) +################################################################################ +set_property -dict { PACKAGE_PIN C4 IOSTANDARD LVCMOS33 } [get_ports { a2_p }]; +set_property -dict { PACKAGE_PIN B4 IOSTANDARD LVCMOS33 } [get_ports { a2_n }]; +set_property -dict { PACKAGE_PIN B1 IOSTANDARD LVCMOS33 } [get_ports { a3_p }]; +set_property -dict { PACKAGE_PIN A1 IOSTANDARD LVCMOS33 } [get_ports { a3_n }]; +set_property -dict { PACKAGE_PIN B3 IOSTANDARD LVCMOS33 } [get_ports { a4_p }]; +set_property -dict { PACKAGE_PIN B2 IOSTANDARD LVCMOS33 } [get_ports { a4_n }]; +set_property -dict { PACKAGE_PIN D14 IOSTANDARD LVCMOS33 } [get_ports { a5_p }]; +set_property -dict { PACKAGE_PIN C14 IOSTANDARD LVCMOS33 } [get_ports { a5_n }]; + ################################################################################ # Arduino/chipKIT shield connector ################################################################################ diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index cf5f775..61fa274 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -149,6 +149,16 @@ entity toplevel is lcd_wr : out std_ulogic; lcd_rst : out std_ulogic; + -- Differential analog inputs from touchscreen + a2_p : in std_ulogic; + a2_n : in std_ulogic; + a3_p : in std_ulogic; + a3_n : in std_ulogic; + a4_p : in std_ulogic; + a4_n : in std_ulogic; + a5_p : in std_ulogic; + a5_n : in std_ulogic; + -- DRAM wires ddram_a : out std_ulogic_vector(13 downto 0); ddram_ba : out std_ulogic_vector(2 downto 0); @@ -875,7 +885,16 @@ begin lcd_rsoe => lcd_rsoe, lcd_cs => lcd_cso, lcd_csoe => lcd_csoe, - lcd_rst => lcd_rst + lcd_rst => lcd_rst, + + a2_p => a2_p, + a2_n => a2_n, + a3_p => a3_p, + a3_n => a3_n, + a4_p => a4_p, + a4_n => a4_n, + a5_p => a5_p, + a5_n => a5_n ); -- lcd_d(0), lcd_d(1), lcd_rs, lcd_cs are used for the touchscreen -- interface and hence have individual output enables. From 81792f599bbc42f998bf1c602712bb6f6213c2cd Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 31 Dec 2021 18:09:54 +1100 Subject: [PATCH 10/10] arty a7: Connect SD card interface to microSD socket on LCD touchscreen board If the generic USE_LCD is false, the first SD card controller (mmcblk0 in Linux) is connected to pmod HA; if USE_LCD is true, it is connected to the SD card slot on the touchscreen/LCD panel. Signed-off-by: Paul Mackerras --- fpga/arty_a7.xdc | 28 +++++++---- fpga/top-arty.vhdl | 117 +++++++++++++++++++++++++++++++-------------- 2 files changed, 100 insertions(+), 45 deletions(-) diff --git a/fpga/arty_a7.xdc b/fpga/arty_a7.xdc index 6490f5d..2c42303 100644 --- a/fpga/arty_a7.xdc +++ b/fpga/arty_a7.xdc @@ -82,14 +82,14 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*/spi_rxtx/input_ #set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { pmod_ja_10 }]; # connection to Digilent PmodSD on JA -set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard_data[3] }]; -set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard_cmd }]; -set_property -dict { PACKAGE_PIN A11 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard_data[0] }]; -set_property -dict { PACKAGE_PIN D12 IOSTANDARD LVCMOS33 SLEW FAST } [get_ports { sdcard_clk }]; -set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard_data[1] }]; -set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { sdcard_data[2] }]; -set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { sdcard_cd }]; -#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { sdcard_wp }]; +set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ja_sdcard_data[3] }]; +set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ja_sdcard_cmd }]; +set_property -dict { PACKAGE_PIN A11 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ja_sdcard_data[0] }]; +set_property -dict { PACKAGE_PIN D12 IOSTANDARD LVCMOS33 SLEW FAST } [get_ports { ja_sdcard_clk }]; +set_property -dict { PACKAGE_PIN D13 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ja_sdcard_data[1] }]; +set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ja_sdcard_data[2] }]; +set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { ja_sdcard_cd }]; +#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { ja_sdcard_wp }]; # Put registers into IOBs to improve timing set_property IOB true [get_cells -hierarchical -filter {NAME =~*.litesdcard/sdpads_data_i_reg*}] @@ -198,6 +198,18 @@ set_property -dict { PACKAGE_PIN B2 IOSTANDARD LVCMOS33 } [get_ports { a4_n }]; set_property -dict { PACKAGE_PIN D14 IOSTANDARD LVCMOS33 } [get_ports { a5_p }]; set_property -dict { PACKAGE_PIN C14 IOSTANDARD LVCMOS33 } [get_ports { a5_n }]; +################################################################################ +# connection to micro SD card socket on touchscreen board +################################################################################ +set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ts_sdcard_data[3] }]; +set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ts_sdcard_cmd }]; +set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ts_sdcard_data[0] }]; +set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 SLEW FAST } [get_ports { ts_sdcard_clk }]; +set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ts_sdcard_data[1] }]; +set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 SLEW FAST PULLUP TRUE } [get_ports { ts_sdcard_data[2] }]; +set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 PULLUP TRUE } [get_ports { ts_sdcard_cd }]; +#set_property -dict { PACKAGE_PIN K16 IOSTANDARD LVCMOS33 } [get_ports { ts_sdcard_wp }]; + ################################################################################ # Arduino/chipKIT shield connector ################################################################################ diff --git a/fpga/top-arty.vhdl b/fpga/top-arty.vhdl index 61fa274..3d5f3f3 100644 --- a/fpga/top-arty.vhdl +++ b/fpga/top-arty.vhdl @@ -125,11 +125,17 @@ entity toplevel is eth_col : in std_ulogic; eth_crs : in std_ulogic; - -- SD card - sdcard_data : inout std_ulogic_vector(3 downto 0); - sdcard_cmd : inout std_ulogic; - sdcard_clk : out std_ulogic; - sdcard_cd : in std_ulogic; + -- SD card pmod on JA + ja_sdcard_data : inout std_ulogic_vector(3 downto 0); + ja_sdcard_cmd : inout std_ulogic; + ja_sdcard_clk : out std_ulogic; + ja_sdcard_cd : in std_ulogic; + + -- SD card slot on touchscreen/LCD board + ts_sdcard_data : inout std_ulogic_vector(3 downto 0); + ts_sdcard_cmd : inout std_ulogic; + ts_sdcard_clk : out std_ulogic; + ts_sdcard_cd : in std_ulogic; -- Second SD card sdcard2_data : inout std_ulogic_vector(3 downto 0); @@ -722,38 +728,75 @@ begin signal sdc1_activity : std_ulogic := '0'; begin - litesdcard : litesdcard_core - port map ( - clk => system_clk, - rst => periph_rst, - wb_ctrl_adr => wb_sdcard_adr, - wb_ctrl_dat_w => wb_ext_io_in.dat, - wb_ctrl_dat_r => wb_sdcard_out.dat, - wb_ctrl_sel => wb_ext_io_in.sel, - wb_ctrl_cyc => wb_sdcard_cyc, - wb_ctrl_stb => wb_ext_io_in.stb, - wb_ctrl_ack => wb_sdcard_out.ack, - wb_ctrl_we => wb_ext_io_in.we, - wb_ctrl_cti => "000", - wb_ctrl_bte => "00", - wb_ctrl_err => open, - wb_dma_adr => wb_sddma1_nr.adr, - wb_dma_dat_w => wb_sddma1_nr.dat, - wb_dma_dat_r => wb_sddma_ir.dat, - wb_dma_sel => wb_sddma1_nr.sel, - wb_dma_cyc => wb_sddma1_nr.cyc, - wb_dma_stb => wb_sddma1_nr.stb, - wb_dma_ack => wb_sddma1_ack, - wb_dma_we => wb_sddma1_nr.we, - wb_dma_cti => open, - wb_dma_bte => open, - wb_dma_err => '0', - sdcard_data => sdcard_data, - sdcard_cmd => sdcard_cmd, - sdcard_clk => sdcard_clk, - sdcard_cd => sdcard_cd, - irq => ext_irq_sdcard - ); + sdcard_ja: if not USE_LCD generate + litesdcard : litesdcard_core + port map ( + clk => system_clk, + rst => periph_rst, + wb_ctrl_adr => wb_sdcard_adr, + wb_ctrl_dat_w => wb_ext_io_in.dat, + wb_ctrl_dat_r => wb_sdcard_out.dat, + wb_ctrl_sel => wb_ext_io_in.sel, + wb_ctrl_cyc => wb_sdcard_cyc, + wb_ctrl_stb => wb_ext_io_in.stb, + wb_ctrl_ack => wb_sdcard_out.ack, + wb_ctrl_we => wb_ext_io_in.we, + wb_ctrl_cti => "000", + wb_ctrl_bte => "00", + wb_ctrl_err => open, + wb_dma_adr => wb_sddma1_nr.adr, + wb_dma_dat_w => wb_sddma1_nr.dat, + wb_dma_dat_r => wb_sddma_ir.dat, + wb_dma_sel => wb_sddma1_nr.sel, + wb_dma_cyc => wb_sddma1_nr.cyc, + wb_dma_stb => wb_sddma1_nr.stb, + wb_dma_ack => wb_sddma1_ack, + wb_dma_we => wb_sddma1_nr.we, + wb_dma_cti => open, + wb_dma_bte => open, + wb_dma_err => '0', + sdcard_data => ja_sdcard_data, + sdcard_cmd => ja_sdcard_cmd, + sdcard_clk => ja_sdcard_clk, + sdcard_cd => ja_sdcard_cd, + irq => ext_irq_sdcard + ); + end generate; + + sdcard_ts: if USE_LCD generate + litesdcard : litesdcard_core + port map ( + clk => system_clk, + rst => periph_rst, + wb_ctrl_adr => wb_sdcard_adr, + wb_ctrl_dat_w => wb_ext_io_in.dat, + wb_ctrl_dat_r => wb_sdcard_out.dat, + wb_ctrl_sel => wb_ext_io_in.sel, + wb_ctrl_cyc => wb_sdcard_cyc, + wb_ctrl_stb => wb_ext_io_in.stb, + wb_ctrl_ack => wb_sdcard_out.ack, + wb_ctrl_we => wb_ext_io_in.we, + wb_ctrl_cti => "000", + wb_ctrl_bte => "00", + wb_ctrl_err => open, + wb_dma_adr => wb_sddma1_nr.adr, + wb_dma_dat_w => wb_sddma1_nr.dat, + wb_dma_dat_r => wb_sddma_ir.dat, + wb_dma_sel => wb_sddma1_nr.sel, + wb_dma_cyc => wb_sddma1_nr.cyc, + wb_dma_stb => wb_sddma1_nr.stb, + wb_dma_ack => wb_sddma1_ack, + wb_dma_we => wb_sddma1_nr.we, + wb_dma_cti => open, + wb_dma_bte => open, + wb_dma_err => '0', + sdcard_data => ts_sdcard_data, + sdcard_cmd => ts_sdcard_cmd, + sdcard_clk => ts_sdcard_clk, + sdcard_cd => ts_sdcard_cd, + irq => ext_irq_sdcard + ); + end generate; litesdcard2 : litesdcard_core port map (