zynq-sandbox

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 20db1eceb0d6fed70d904296e7d8992e2da424c5
parent 87ef6109809d9325d53d4f39cf8615f9cc537c11
Author: Brian Swetland <swetland@frotz.net>
Date:   Sun, 27 Jul 2014 20:48:03 -0700

eth-capture and friends: capture inbound frames to axi

axi_dma_writer: commit 1-16 word bursts over axi
pkt_bytes_to_words: convert bytestream to wordstream
eth_capture: capture packets to a 4MB ring (currently at 0x10000000)

- packets are located at 4k offsets
- first 4 words of each packet are
  - bytecount
  - timestamp(low)
  - timestamp(high)
  - status(1=packet written)
- data starts at 64 bytes in to each packet

Diffstat:
Ahdl/axi_dma_writer.sv | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahdl/eth_capture.sv | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahdl/pkt_bytes_to_words.sv | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 520 insertions(+), 0 deletions(-)

diff --git a/hdl/axi_dma_writer.sv b/hdl/axi_dma_writer.sv @@ -0,0 +1,150 @@ +// Copyright 2014 Brian Swetland <swetland@frotz.net> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +`timescale 1ns/1ps + +// on the clock where start=1 +// addr is the first address of the transfer +// burstlen is the transfer wordcount minus 1 +// data is the first word of the transfer +// on the clock where advance=1 +// data advances to the next word of the transfer +// busy is asserted from the cycle after start until transfer complete + +module axi_dma_writer( + input clk, + axi_ifc.master m, + input start, + input [31:0]addr, + input [3:0]burstlen, + input [31:0]data, + output reg busy = 0, + output advance + ); + +localparam STATE_IDLE = 4'd0; +localparam STATE_WADDR = 4'd1; +localparam STATE_WDATA = 4'd2; +localparam STATE_ACK = 4'd3; + +initial m.awvalid = 0; +initial m.wvalid = 0; + +reg [31:0]waddr = 0; +reg [31:0]waddr_next; + +reg [3:0]state = STATE_IDLE; +reg [3:0]state_next; + +reg awvalid_next; +reg wvalid_next; +reg bready_next; +reg wlast_next; + +reg [3:0]count = 15; +reg [3:0]count_next; + +reg busy_next; + +wire count_is_zero = (count == 4'h0); +wire count_is_one = (count == 4'h1); + +assign advance = m.wready & busy; + +always_comb begin + state_next = state; + count_next = count; + waddr_next = waddr; + busy_next = busy; + awvalid_next = 0; + wvalid_next = 0; + bready_next = 0; + wlast_next = 0; + + case (state) + STATE_IDLE: begin + if (start) begin + state_next = STATE_WADDR; + count_next = burstlen; + waddr_next = addr; + busy_next = 1; + awvalid_next = 1; + end + end + STATE_WADDR: begin + if (m.awready) begin + state_next = STATE_WDATA; + wvalid_next = 1; + wlast_next = count_is_zero; + end else begin + awvalid_next = 1; + end + end + STATE_WDATA: begin + if (count_is_zero) begin + if (m.wready) begin + state_next = STATE_ACK; + bready_next = 1; + end else begin + wvalid_next = 1; + wlast_next = 1; + end + end else begin + wvalid_next = 1; + if (m.wready) begin + if (count_is_one) begin + wlast_next = 1; + end + count_next = count - 1; + end + end + end + STATE_ACK: begin + if (m.bvalid) begin + state_next = STATE_IDLE; + busy_next = 0; + end else begin + bready_next = 1; + end + end + default: state_next = STATE_IDLE; + endcase +end + +assign m.awid = 0; +assign m.awburst = 1; +assign m.awcache = 0; +assign m.awsize = 2; +assign m.awlen = count; +assign m.awlock = 0; + +assign m.awaddr = waddr; +assign m.wdata = data; +assign m.wstrb = 4'b1111; + +always_ff @(posedge clk) begin + state <= state_next; + count <= count_next; + waddr <= waddr_next; + busy <= busy_next; + m.awvalid <= awvalid_next; + m.wvalid <= wvalid_next; + m.bready <= bready_next; + m.wlast <= wlast_next; +end + +assign m.arvalid = 0; +assign m.rvalid = 0; + +endmodule diff --git a/hdl/eth_capture.sv b/hdl/eth_capture.sv @@ -0,0 +1,227 @@ +// Copyright 2014 Brian Swetland <swetland@frotz.net> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +`timescale 1ns / 1ps + +module eth_capture( + // interface from eth_rmii_rx + input clk50, + input rxsop, + input rxeop, + input [7:0]rxdata, + input rxvalid, + + // interface to axi + input clk, + input reset, + axi_ifc axi_dma + ); + + +wire [31:0]w_data; +wire w_valid; +wire w_eop; +wire [11:0]bytecount; + +reg [63:0]clk50_count = 0; +reg [63:0]rxtimestamp = 0; +always @(posedge clk50) begin + clk50_count <= clk50_count + 1; + if (rxsop) begin + rxtimestamp <= clk50_count; + end +end + +pkt_bytes_to_words cap0( + .clk(clk50), + .rxdata(rxdata), + .rxvalid(rxvalid), + .rxeop(rxeop), + .data(w_data), + .bytecount(bytecount), + .eop(w_eop), + .valid(w_valid) + ); + +typedef enum { DATA, EOP0, EOP1, EOP2 } state_t; +state_t state = DATA; +state_t next_state; + +reg [19:0]cfifo_in = 0; +reg [31:0]dfifo_in = 0; +reg [19:0]next_cfifo_in; +reg [31:0]next_dfifo_in; +reg cfifo_wr = 0; +reg dfifo_wr = 0; +reg next_cfifo_wr; +reg next_dfifo_wr; +reg [3:0]dcount = 15; +reg [3:0]next_dcount; + +// ADDR: window(10) packet(10) chunk(6) off(6) +reg [9:0]dbase = 0; +reg [9:0]next_dbase; +reg [5:0]daddr = 1; +reg [5:0]next_daddr; + +wire [3:0]dcount_plus_one = (dcount + 1); + +always_comb begin + next_state = state; + next_dcount = dcount; + next_daddr = daddr; + next_dbase = dbase; + next_cfifo_in = cfifo_in; + next_dfifo_in = dfifo_in; + next_cfifo_wr = 0; + next_dfifo_wr = 0; + case (state) + DATA: begin + if (w_valid) begin + next_dcount = dcount_plus_one; + next_dfifo_in = w_data; + next_dfifo_wr = 1; + if (dcount_plus_one == 15) begin + next_cfifo_in = { 4'd15, dbase, daddr }; + next_cfifo_wr = 1; + next_daddr = daddr + 1; + end + end else if (w_eop) begin + next_state = EOP0; + next_dfifo_in = { 20'h00000, bytecount }; + next_dfifo_wr = 1; + if (dcount != 15) begin + next_cfifo_in = { dcount, dbase, daddr }; + next_cfifo_wr = 1; + end + end + end + EOP0: begin + next_state = EOP1; + next_dfifo_in = rxtimestamp[31:0]; + next_dfifo_wr = 1; + end + EOP1: begin + next_state = EOP2; + next_dfifo_in = rxtimestamp[63:32]; + next_dfifo_wr = 1; + end + EOP2: begin + next_state = DATA; + next_dcount = 15; + next_daddr = 1; + next_dbase = dbase + 1; + next_dfifo_in = 32'h00000001; // status + next_dfifo_wr = 1; + next_cfifo_in = { 4'd3, dbase, 6'd0 }; + next_cfifo_wr = 1; + end + endcase +end + +always_ff @(posedge clk50) begin + state <= next_state; + dcount <= next_dcount; + daddr <= next_daddr; + dbase <= next_dbase; + cfifo_in <= next_cfifo_in; + dfifo_in <= next_dfifo_in; + cfifo_wr <= next_cfifo_wr; + dfifo_wr <= next_dfifo_wr; +end + +reg fifo_reset = 0; + +wire [31:0]dfifo_data; +reg dfifo_rd = 0; +wire dfifo_empty; +wire dfifo_active; + +wire [19:0]cfifo_data; +reg cfifo_rd = 0; +wire cfifo_empty; +wire cfifo_active; + +// on reset request, assert fifo_reset until both fifos +// have started the sequence (fifo_active=0) +always @(posedge clk) begin + if (dfifo_active & cfifo_active) begin + if (reset) begin + fifo_reset <= 1; + end + end else if (~dfifo_active & ~cfifo_active) begin + fifo_reset <= 0; + end +end + +// CMD: wrstatus wrdata count(12) +xilinx_async_fifo #( + .WIDTH(32) + ) dfifo ( + .wrclk(clk50), + .rdclk(clk), + .reset(fifo_reset), + .wr_data(dfifo_in), + .wr_en(dfifo_wr), + .rd_en(dfifo_rd), + .rd_data(dfifo_data), + .o_empty(dfifo_empty), + .o_ready(), + .o_active(dfifo_active) + ); + +// CMD: burst(4) addr(16) -- addr is A[21:6] + +xilinx_async_fifo #( + .WIDTH(20) + ) cfifo ( + .wrclk(clk50), + .rdclk(clk), + .reset(fifo_reset), + .wr_data(cfifo_in), + .wr_en(cfifo_wr), + .rd_en(cfifo_rd), + .rd_data(cfifo_data), + .o_empty(cfifo_empty), + .o_ready(), + .o_active(cfifo_active) + ); + +reg [31:0]dma_base = 32'h10000000; + +wire [31:0]dma_addr = { dma_base[31:22], cfifo_data[15:0], 6'd0 }; +wire [3:0]dma_len = cfifo_data[19:16]; +wire dma_start = (~cfifo_empty) & (~dma_busy); +wire dma_busy; + +always @(posedge clk) begin + if (dma_start) begin + cfifo_rd <= 1; + end else begin + cfifo_rd <= 0; + end +end + +axi_dma_writer dma0( + .clk(clk), + .m(axi_dma), + .addr(dma_addr), + .burstlen(dma_len), + .start(dma_start), + .busy(dma_busy), + .data(dfifo_data), + .advance(dfifo_rd) + ); + +endmodule diff --git a/hdl/pkt_bytes_to_words.sv b/hdl/pkt_bytes_to_words.sv @@ -0,0 +1,143 @@ +// Copyright 2014 Brian Swetland <swetland@frotz.net> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +`timescale 1ns / 1ps + + +// converts packet bytestream to wordstream +// +// input: rxdata accepted when rxvalid=1 +// rxeop terminates stream +// rxeop may not happen on same clock as rxvalid +// output: data and bytecount presented with valid=1 +// eop=1 indicates end of stream +// eop and valid will not happen on the same clock + +module pkt_bytes_to_words( + input clk, + input [7:0]rxdata, + input rxvalid, + input rxeop, + + output [31:0]data, + output [11:0]bytecount, + output reg valid = 0, + output reg eop = 0 + ); + +typedef enum { IDLE, BYTE0, BYTE1, BYTE2, BYTE3, EOP } state_t; +state_t state = IDLE; +state_t next_state; + +reg [11:0]count = 0; +wire [11:0]count_plus_one = (count + 1); +reg [11:0]next_count; +reg [7:0]byte0 = 0; +reg [7:0]next_byte0; +reg [7:0]byte1 = 0; +reg [7:0]next_byte1; +reg [7:0]byte2 = 0; +reg [7:0]next_byte2; +reg [7:0]byte3 = 0; +reg [7:0]next_byte3; + +reg next_valid; +reg next_eop; + +assign data = { byte3, byte2, byte1, byte0 }; +assign bytecount = count; + +always_comb begin + next_state = state; + next_count = count; + next_byte0 = byte0; + next_byte1 = byte1; + next_byte2 = byte2; + next_byte3 = byte3; + next_valid = 0; + next_eop = 0; + + case (state) + IDLE: begin + if (rxvalid) begin + next_state = BYTE0; + next_byte0 = rxdata; + next_byte1 = 0; + next_byte2 = 0; + next_byte3 = 0; + next_count = 1; + end + end + BYTE0: begin + if (rxvalid) begin + next_state = BYTE1; + next_byte1 = rxdata; + next_count = count_plus_one; + end else if (rxeop) begin + next_state = EOP; + next_valid = 1; + end + end + BYTE1: begin + if (rxvalid) begin + next_state = BYTE2; + next_byte2 = rxdata; + next_count = count_plus_one; + end else if (rxeop) begin + next_state = EOP; + next_valid = 1; + end + end + BYTE2: begin + if (rxvalid) begin + next_state = BYTE3; + next_byte3 = rxdata; + next_count = count_plus_one; + next_valid = 1; + end else if (rxeop) begin + next_state = EOP; + next_valid = 1; + end + end + BYTE3: begin + if (rxvalid) begin + next_state = BYTE0; + next_byte0 = rxdata; + next_byte1 = 0; + next_byte2 = 0; + next_byte3 = 0; + next_count = count_plus_one; + end else if (rxeop) begin + next_state = EOP; + end + end + EOP: begin + next_state = IDLE; + next_eop = 1; + end + endcase +end + +always_ff @(posedge clk) begin + state <= next_state; + count <= next_count; + byte0 <= next_byte0; + byte1 <= next_byte1; + byte2 <= next_byte2; + byte3 <= next_byte3; + valid <= next_valid; + eop <= next_eop; +end + +endmodule