diff --git a/Makefile b/Makefile index 53aeb42..fa504fc 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ pixelflut.frames: pixelflut.fasm pixelflut.fasm: arty_a7_35t.xdc pixelflut.json nextpnr-xilinx --chipdb "$(CHIPDB_DIR)/$(PART).bin" --fasm $@ --json pixelflut.json --xdc arty_a7_35t.xdc -pixelflut.json: pixelflut.v dvi.v xc7_bram.v +pixelflut.json: pixelflut.v dvi.v ethernet_smi.v pingxelflut.v xc7_bram.v yosys -q -p 'synth_xilinx -top pixelflut; write_json $@' $^ dvi_tb.vcd: dvi_tb.vvp diff --git a/ethernet_smi.v b/ethernet_smi.v new file mode 100644 index 0000000..56849fb --- /dev/null +++ b/ethernet_smi.v @@ -0,0 +1,28 @@ +module ethernet_smi #( + parameter CLK_DIVIDE = 10, +) ( + input clk, + + input mdio_i, + output reg mdio_o, + output reg mdio_en, + output reg mdc, +); + reg [3:0] ctr; + + initial begin + mdio_o <= 1'b1; + mdio_en <= 1'b0; + + ctr <= 4'b0; + end + + always @(posedge clk) begin + if (ctr == CLK_DIVIDE) begin + mdc <= ~mdc; + ctr <= 4'b0; + end else begin + ctr <= ctr + 1; + end + end +endmodule diff --git a/pingxelflut.v b/pingxelflut.v new file mode 100644 index 0000000..b81b6e0 --- /dev/null +++ b/pingxelflut.v @@ -0,0 +1,216 @@ +module pingxelflut #( + parameter MAC_ADDRESS = 48'h02_00_00_00_00_00, + + parameter SCREEN_WIDTH = 346, +) ( + input rx_clk, + input [3:0] rxd, + input rx_dv, + input rx_er, + + output reg bus_clk, + output reg [15:0] bus_data, + output reg [23:0] bus_addr, + output reg [1:0] bus_sel, +); + localparam PREAMBLE = 7'b0000000; + localparam DEST_0 = 7'b0000001; + localparam DEST_1 = 7'b0000010; + localparam DEST_2 = 7'b0000011; + localparam DEST_3 = 7'b0000100; + localparam DEST_4 = 7'b0000101; + localparam DEST_5 = 7'b0000110; + localparam DEST_6 = 7'b0000111; + localparam DEST_7 = 7'b0001000; + localparam DEST_8 = 7'b0001001; + localparam DEST_9 = 7'b0001010; + localparam DEST_A = 7'b0001011; + localparam DEST_B = 7'b0001100; + localparam SRC_0 = 7'b0001101; + localparam SRC_1 = 7'b0001110; + localparam SRC_2 = 7'b0001111; + localparam SRC_3 = 7'b0010000; + localparam SRC_4 = 7'b0010001; + localparam SRC_5 = 7'b0010010; + localparam SRC_6 = 7'b0010011; + localparam SRC_7 = 7'b0010100; + localparam SRC_8 = 7'b0010101; + localparam SRC_9 = 7'b0010110; + localparam SRC_A = 7'b0010111; + localparam SRC_B = 7'b0011000; + localparam ETHER_TYPE_0 = 7'b0011001; + localparam ETHER_TYPE_1 = 7'b0011010; + localparam ETHER_TYPE_2 = 7'b0011011; + localparam ETHER_TYPE_3 = 7'b0011100; + + localparam IGNORE = 7'b0011111; + + localparam VERSION = 7'b0100000; + localparam TRAFFIC_CLASS_0 = 7'b0100001; + localparam TRAFFIC_CLASS_1 = 7'b0100010; + localparam FLOW_LABEL_0 = 7'b0100011; + localparam FLOW_LABEL_1 = 7'b0100100; + localparam FLOW_LABEL_2 = 7'b0100101; + localparam FLOW_LABEL_3 = 7'b0100110; + localparam FLOW_LABEL_4 = 7'b0100111; + localparam PAYLOAD_LENGTH_0 = 7'b0101000; + localparam PAYLOAD_LENGTH_1 = 7'b0101001; + localparam PAYLOAD_LENGTH_2 = 7'b0101010; + localparam PAYLOAD_LENGTH_3 = 7'b0101011; + localparam NEXT_HEADER_0 = 7'b0101100; + localparam NEXT_HEADER_1 = 7'b0101101; + localparam HOP_LIMIT_0 = 7'b0101110; + localparam HOP_LIMIT_1 = 7'b0101111; + localparam SRC_ADDR_0 = 7'b0110000; + localparam DEST_PREFIX_0 = 7'b1010000; + localparam X_0 = 7'b1110000; + localparam X_1 = 7'b1110001; + localparam X_2 = 7'b1110010; + localparam X_3 = 7'b1110011; + localparam Y_0 = 7'b1110100; + localparam Y_1 = 7'b1110101; + localparam Y_2 = 7'b1110110; + localparam Y_3 = 7'b1110111; + localparam RED_LOW = 7'b1111000; + localparam RED_HIGH = 7'b1111001; + localparam GREEN_LOW = 7'b1111010; + localparam GREEN_HIGH = 7'b1111011; + localparam BLUE_LOW = 7'b1111100; + localparam BLUE_HIGH = 7'b1111101; + localparam PADDING_LOW = 7'b1111110; + localparam PADDING_HIGH = 7'b1111111; + + localparam ETHER_TYPE_IPV6 = 16'h86dd; + + reg [6:0] state; + reg broadcast; + reg [22:0] pixel_addr; + reg [16:0] pixel_data; + + wire maybe_broadcast; + + assign maybe_broadcast = broadcast && rxd == 4'hf; + + initial begin + bus_clk <= 0; + bus_data <= 16'b0; + bus_addr <= 24'b0; + bus_sel <= 2'b0; + + state <= PREAMBLE; + broadcast <= 0; + pixel_addr <= 23'b0; + pixel_data <= 16'b0; + end + + always @(posedge rx_clk) begin + if (rx_dv && ~rx_er) begin + if (state >= DEST_0 && state < ETHER_TYPE_3 || state >= VERSION && state < PADDING_HIGH) state <= state + 1; + else if (state == ETHER_TYPE_3) state <= VERSION; + else if (state == PADDING_HIGH) state <= IGNORE; + + broadcast <= maybe_broadcast; + + case (state) + PREAMBLE: begin + if (rxd == 4'hd) begin + state <= DEST_0; + broadcast <= 1; + end + end + DEST_0: if (rxd != MAC_ADDRESS[43:40] && ~maybe_broadcast) state <= IGNORE; + DEST_1: if (rxd != MAC_ADDRESS[47:44] && ~maybe_broadcast) state <= IGNORE; + DEST_2: if (rxd != MAC_ADDRESS[35:32] && ~maybe_broadcast) state <= IGNORE; + DEST_3: if (rxd != MAC_ADDRESS[39:36] && ~maybe_broadcast) state <= IGNORE; + DEST_4: if (rxd != MAC_ADDRESS[27:24] && ~maybe_broadcast) state <= IGNORE; + DEST_5: if (rxd != MAC_ADDRESS[31:28] && ~maybe_broadcast) state <= IGNORE; + DEST_6: if (rxd != MAC_ADDRESS[19:16] && ~maybe_broadcast) state <= IGNORE; + DEST_7: if (rxd != MAC_ADDRESS[23:20] && ~maybe_broadcast) state <= IGNORE; + DEST_8: if (rxd != MAC_ADDRESS[11: 8] && ~maybe_broadcast) state <= IGNORE; + DEST_9: if (rxd != MAC_ADDRESS[15:12] && ~maybe_broadcast) state <= IGNORE; + DEST_A: if (rxd != MAC_ADDRESS[ 3: 0] && ~maybe_broadcast) state <= IGNORE; + DEST_B: if (rxd != MAC_ADDRESS[ 7: 4] && ~maybe_broadcast) state <= IGNORE; + ETHER_TYPE_0: if (rxd != ETHER_TYPE_IPV6[11: 8]) state <= IGNORE; + ETHER_TYPE_1: if (rxd != ETHER_TYPE_IPV6[15:12]) state <= IGNORE; + ETHER_TYPE_2: if (rxd != ETHER_TYPE_IPV6[ 3: 0]) state <= IGNORE; + ETHER_TYPE_3: if (rxd != ETHER_TYPE_IPV6[ 7: 4]) state <= IGNORE; + VERSION: if (rxd != 4'b0110) state <= IGNORE; + X_0: pixel_addr[11:8] <= rxd; + X_2: pixel_addr[ 3:0] <= rxd; + X_3: pixel_addr[ 7:4] <= rxd; + Y_2: pixel_addr <= pixel_addr + rxd * SCREEN_WIDTH; + Y_3: pixel_addr <= pixel_addr + {rxd, 4'b0} * SCREEN_WIDTH; + RED_LOW: begin + if (pixel_addr[0]) pixel_data[11:8] <= rxd; + else pixel_data[ 3:0] <= rxd; + end + RED_HIGH: begin + if (pixel_addr[0]) pixel_data[15:12] <= rxd; + else pixel_data[ 7: 4] <= rxd; + end + GREEN_LOW: begin + if (pixel_addr[0]) begin + pixel_data[ 3:0] <= rxd; + end else begin + bus_data <= pixel_data; + bus_addr <= {pixel_addr, 1'b1}; + bus_sel <= 2'b01; + + pixel_data[11:8] <= rxd; + end + end + GREEN_HIGH: begin + if (pixel_addr[0]) begin + pixel_data[ 7: 4] <= rxd; + end else begin + bus_clk <= 1; + + pixel_data[15:12] <= rxd; + end + end + BLUE_LOW: begin + if (pixel_addr[0]) begin + bus_data <= pixel_data; + bus_addr <= {pixel_addr, 1'b0}; + bus_sel <= 2'b11; + + pixel_data[11:8] <= rxd; + end else begin + bus_clk <= 0; + + pixel_data[ 3:0] <= rxd; + end + end + BLUE_HIGH: begin + if (pixel_addr[0]) begin + bus_clk <= 1; + + pixel_data[15:12] <= rxd; + end else begin + pixel_data[ 7: 4] <= rxd; + end + end + PADDING_LOW: begin + bus_data <= pixel_data; + + if (pixel_addr[0]) begin + bus_clk <= 0; + bus_addr <= {pixel_addr, 1'b1}; + bus_sel <= 2'b10; + end else begin + bus_addr <= {pixel_addr, 1'b0}; + bus_sel <= 2'b11; + end + end + PADDING_HIGH: bus_clk <= 1; + IGNORE: bus_clk <= 0; + endcase + end else begin + bus_clk <= 0; + + state <= PREAMBLE; + pixel_addr <= 23'b0; + pixel_data <= 16'b0; + end + end +endmodule diff --git a/pixelflut.v b/pixelflut.v index 37fe57d..2d6cae6 100644 --- a/pixelflut.v +++ b/pixelflut.v @@ -10,14 +10,30 @@ module pixelflut ( output dvi_de, output dvi_hs, output dvi_vs, + + output eth_mdc, + inout eth_mdio, + output eth_ref_clk, + output eth_rstn, + input eth_rx_clk, + input eth_rx_dv, + input [3:0] eth_rxd, + input eth_rxerr, + input eth_tx_clk, + output eth_tx_en, + output [3:0] eth_txd, ); reg [31:0] ctr; - reg [2:0] led0_state = 3'b0; + reg [2:0] led0_state; assign led0_r = led0_state[0]; assign led0_g = led0_state[1]; assign led0_b = led0_state[2]; + initial begin + led0_state <= 3'b0; + end + always @(posedge sys_clk) begin if (ctr == 32'd50_000_000) begin ctr <= 32'b0; @@ -48,13 +64,62 @@ module pixelflut ( .vs (dvi_vs), ); + reg [1:0] eth_clk_div; + + assign eth_ref_clk = eth_clk_div[1]; + + initial begin + eth_clk_div <= 2'b0; + end + + always @(posedge sys_clk) begin + eth_clk_div <= eth_clk_div + 1; + end + + assign eth_rstn = 1; + assign eth_tx_en = 0; + assign eth_txd = 4'b0; + + wire eth_mdio_i, eth_mdio_o, eth_mdio_en; + + IOBUF eth_mdio_buf ( + .I (eth_mdio_o), + .IO(eth_mdio), + .O (eth_mdio_i), + .T (eth_mdio_en), + ); + + ethernet_smi smi ( + .clk (sys_clk), + .mdio_i (eth_mdio_i), + .mdio_o (eth_mdio_o), + .mdio_en(eth_mdio_en), + .mdc (eth_mdc), + ); + + wire eth_bus_clk; + wire [15:0] eth_bus_data; + wire [23:0] eth_bus_addr; + wire [1:0] eth_bus_sel; + + pingxelflut eth ( + .rx_clk (eth_rx_clk), + .rxd (eth_rxd), + .rx_dv (eth_rx_dv), + .rx_er (eth_rxerr), + .bus_clk (eth_bus_clk), + .bus_data(eth_bus_data), + .bus_addr(eth_bus_addr), + .bus_sel (eth_bus_sel), + ); + xc7_bram ram ( .out_clk (dvi_bus_clk), .out_data(dvi_bus_data), .out_addr(dvi_bus_addr), - .in_clk (), - .in_data (), - .in_addr (), - .in_wren (), + .in_clk (eth_bus_clk), + .in_data (eth_bus_data), + .in_addr (eth_bus_addr), + .in_wren (eth_bus_sel), ); endmodule