From 6cb5f880735dc8dd6af63931178f5936dbdd093b Mon Sep 17 00:00:00 2001 From: Andreas Olofsson Date: Fri, 6 Nov 2015 06:56:56 -0500 Subject: [PATCH] Moving block deisgns into a single Parallella module - Easier to maintain - Better sandbox --- parallella/hdl/parallella_basic.v | 335 ++++++++++++++++++++++++++++++ parallella/hdl/pgpio.v | 136 ++++++++++++ parallella/hdl/pi2c.v | 85 ++++++++ 3 files changed, 556 insertions(+) create mode 100644 parallella/hdl/parallella_basic.v create mode 100644 parallella/hdl/pgpio.v create mode 100644 parallella/hdl/pi2c.v diff --git a/parallella/hdl/parallella_basic.v b/parallella/hdl/parallella_basic.v new file mode 100644 index 0000000..5dc2ff4 --- /dev/null +++ b/parallella/hdl/parallella_basic.v @@ -0,0 +1,335 @@ +//NOTE: module name differs from file name to simplify Vivado block design +//Many verilog versions to one block design... +module parallella_top(/*AUTOARG*/ + // Outputs + txo_lclk_p, txo_lclk_n, txo_frame_p, txo_frame_n, txo_data_p, + txo_data_n, timeout, s_axi_wready, s_axi_rvalid, s_axi_rresp, + s_axi_rlast, s_axi_rid, s_axi_rdata, s_axi_bvalid, s_axi_bresp, + s_axi_bid, s_axi_awready, s_axi_arready, rxo_wr_wait_p, + rxo_wr_wait_n, rxo_rd_wait_p, rxo_rd_wait_n, ps_gpio_i, + mailbox_not_empty, mailbox_full, m_axi_wvalid, m_axi_wstrb, + m_axi_wlast, m_axi_wid, m_axi_wdata, m_axi_rready, m_axi_bready, + m_axi_awvalid, m_axi_awsize, m_axi_awqos, m_axi_awprot, + m_axi_awlock, m_axi_awlen, m_axi_awid, m_axi_awcache, + m_axi_awburst, m_axi_awaddr, m_axi_arvalid, m_axi_arsize, + m_axi_arqos, m_axi_arprot, m_axi_arlock, m_axi_arlen, m_axi_arid, + m_axi_arcache, m_axi_arburst, m_axi_araddr, i2c_sda_i, i2c_scl_i, + elink_active, chipid, chip_resetb, cclk_p, cclk_n, + // Inouts + i2c_sda, i2c_scl, gpio_p, gpio_n, + // Inputs + txi_wr_wait_p, txi_wr_wait_n, txi_rd_wait_p, txi_rd_wait_n, + sys_clk, s_axi_wvalid, s_axi_wstrb, s_axi_wlast, s_axi_wid, + s_axi_wdata, s_axi_rready, s_axi_bready, s_axi_awvalid, + s_axi_awsize, s_axi_awqos, s_axi_awprot, s_axi_awlock, s_axi_awlen, + s_axi_awid, s_axi_awcache, s_axi_awburst, s_axi_awaddr, + s_axi_arvalid, s_axi_arsize, s_axi_arqos, s_axi_arprot, + s_axi_arlock, s_axi_arlen, s_axi_arid, s_axi_aresetn, + s_axi_arcache, s_axi_arburst, s_axi_araddr, rxi_lclk_p, rxi_lclk_n, + rxi_frame_p, rxi_frame_n, rxi_data_p, rxi_data_n, reset, ps_gpio_t, + ps_gpio_o, m_axi_wready, m_axi_rvalid, m_axi_rresp, m_axi_rlast, + m_axi_rid, m_axi_rdata, m_axi_bvalid, m_axi_bresp, m_axi_bid, + m_axi_awready, m_axi_arready, m_axi_aresetn, i2c_sda_t, i2c_sda_o, + i2c_scl_t, i2c_scl_o + ); + + parameter AW = 32; + parameter DW = 32; + parameter PW = 104; //packet width + parameter ID = 12'h810; + parameter S_IDW = 12; //ID width for S_AXI + parameter M_IDW = 6; //ID width for M_AXI + parameter IOSTD_ELINK = "LVDS_25"; + parameter ETYPE = 0; + parameter NGPIO = 24; + parameter NPS = 64; //Number of PS signals + + /*AUTOINOUT*/ + // Beginning of automatic inouts (from unused autoinst inouts) + inout [NGPIO-1:0] gpio_n; // To/From pgpio of pgpio.v + inout [NGPIO-1:0] gpio_p; // To/From pgpio of pgpio.v + inout i2c_scl; // To/From pi2c of pi2c.v + inout i2c_sda; // To/From pi2c of pi2c.v + // End of automatics + /*AUTOOUTPUT*/ + // Beginning of automatic outputs (from unused autoinst outputs) + output cclk_n; // From axe_elink of axi_elink.v + output cclk_p; // From axe_elink of axi_elink.v + output chip_resetb; // From axe_elink of axi_elink.v + output [11:0] chipid; // From axe_elink of axi_elink.v + output elink_active; // From axe_elink of axi_elink.v + output i2c_scl_i; // From pi2c of pi2c.v + output i2c_sda_i; // From pi2c of pi2c.v + output [31:0] m_axi_araddr; // From axe_elink of axi_elink.v + output [1:0] m_axi_arburst; // From axe_elink of axi_elink.v + output [3:0] m_axi_arcache; // From axe_elink of axi_elink.v + output [M_IDW-1:0] m_axi_arid; // From axe_elink of axi_elink.v + output [7:0] m_axi_arlen; // From axe_elink of axi_elink.v + output [1:0] m_axi_arlock; // From axe_elink of axi_elink.v + output [2:0] m_axi_arprot; // From axe_elink of axi_elink.v + output [3:0] m_axi_arqos; // From axe_elink of axi_elink.v + output [2:0] m_axi_arsize; // From axe_elink of axi_elink.v + output m_axi_arvalid; // From axe_elink of axi_elink.v + output [31:0] m_axi_awaddr; // From axe_elink of axi_elink.v + output [1:0] m_axi_awburst; // From axe_elink of axi_elink.v + output [3:0] m_axi_awcache; // From axe_elink of axi_elink.v + output [M_IDW-1:0] m_axi_awid; // From axe_elink of axi_elink.v + output [7:0] m_axi_awlen; // From axe_elink of axi_elink.v + output [1:0] m_axi_awlock; // From axe_elink of axi_elink.v + output [2:0] m_axi_awprot; // From axe_elink of axi_elink.v + output [3:0] m_axi_awqos; // From axe_elink of axi_elink.v + output [2:0] m_axi_awsize; // From axe_elink of axi_elink.v + output m_axi_awvalid; // From axe_elink of axi_elink.v + output m_axi_bready; // From axe_elink of axi_elink.v + output m_axi_rready; // From axe_elink of axi_elink.v + output [63:0] m_axi_wdata; // From axe_elink of axi_elink.v + output [M_IDW-1:0] m_axi_wid; // From axe_elink of axi_elink.v + output m_axi_wlast; // From axe_elink of axi_elink.v + output [7:0] m_axi_wstrb; // From axe_elink of axi_elink.v + output m_axi_wvalid; // From axe_elink of axi_elink.v + output mailbox_full; // From axe_elink of axi_elink.v + output mailbox_not_empty; // From axe_elink of axi_elink.v + output [NPS-1:0] ps_gpio_i; // From pgpio of pgpio.v + output rxo_rd_wait_n; // From axe_elink of axi_elink.v + output rxo_rd_wait_p; // From axe_elink of axi_elink.v + output rxo_wr_wait_n; // From axe_elink of axi_elink.v + output rxo_wr_wait_p; // From axe_elink of axi_elink.v + output s_axi_arready; // From axe_elink of axi_elink.v + output s_axi_awready; // From axe_elink of axi_elink.v + output [S_IDW-1:0] s_axi_bid; // From axe_elink of axi_elink.v + output [1:0] s_axi_bresp; // From axe_elink of axi_elink.v + output s_axi_bvalid; // From axe_elink of axi_elink.v + output [31:0] s_axi_rdata; // From axe_elink of axi_elink.v + output [S_IDW-1:0] s_axi_rid; // From axe_elink of axi_elink.v + output s_axi_rlast; // From axe_elink of axi_elink.v + output [1:0] s_axi_rresp; // From axe_elink of axi_elink.v + output s_axi_rvalid; // From axe_elink of axi_elink.v + output s_axi_wready; // From axe_elink of axi_elink.v + output timeout; // From axe_elink of axi_elink.v + output [7:0] txo_data_n; // From axe_elink of axi_elink.v + output [7:0] txo_data_p; // From axe_elink of axi_elink.v + output txo_frame_n; // From axe_elink of axi_elink.v + output txo_frame_p; // From axe_elink of axi_elink.v + output txo_lclk_n; // From axe_elink of axi_elink.v + output txo_lclk_p; // From axe_elink of axi_elink.v + // End of automatics + /*AUTOINPUT*/ + // Beginning of automatic inputs (from unused autoinst inputs) + input i2c_scl_o; // To pi2c of pi2c.v + input i2c_scl_t; // To pi2c of pi2c.v + input i2c_sda_o; // To pi2c of pi2c.v + input i2c_sda_t; // To pi2c of pi2c.v + input m_axi_aresetn; // To axe_elink of axi_elink.v + input m_axi_arready; // To axe_elink of axi_elink.v + input m_axi_awready; // To axe_elink of axi_elink.v + input [M_IDW-1:0] m_axi_bid; // To axe_elink of axi_elink.v + input [1:0] m_axi_bresp; // To axe_elink of axi_elink.v + input m_axi_bvalid; // To axe_elink of axi_elink.v + input [63:0] m_axi_rdata; // To axe_elink of axi_elink.v + input [M_IDW-1:0] m_axi_rid; // To axe_elink of axi_elink.v + input m_axi_rlast; // To axe_elink of axi_elink.v + input [1:0] m_axi_rresp; // To axe_elink of axi_elink.v + input m_axi_rvalid; // To axe_elink of axi_elink.v + input m_axi_wready; // To axe_elink of axi_elink.v + input [NPS-1:0] ps_gpio_o; // To pgpio of pgpio.v + input [NPS-1:0] ps_gpio_t; // To pgpio of pgpio.v + input reset; // To axe_elink of axi_elink.v + input [7:0] rxi_data_n; // To axe_elink of axi_elink.v + input [7:0] rxi_data_p; // To axe_elink of axi_elink.v + input rxi_frame_n; // To axe_elink of axi_elink.v + input rxi_frame_p; // To axe_elink of axi_elink.v + input rxi_lclk_n; // To axe_elink of axi_elink.v + input rxi_lclk_p; // To axe_elink of axi_elink.v + input [31:0] s_axi_araddr; // To axe_elink of axi_elink.v + input [1:0] s_axi_arburst; // To axe_elink of axi_elink.v + input [3:0] s_axi_arcache; // To axe_elink of axi_elink.v + input s_axi_aresetn; // To axe_elink of axi_elink.v + input [S_IDW-1:0] s_axi_arid; // To axe_elink of axi_elink.v + input [7:0] s_axi_arlen; // To axe_elink of axi_elink.v + input [1:0] s_axi_arlock; // To axe_elink of axi_elink.v + input [2:0] s_axi_arprot; // To axe_elink of axi_elink.v + input [3:0] s_axi_arqos; // To axe_elink of axi_elink.v + input [2:0] s_axi_arsize; // To axe_elink of axi_elink.v + input s_axi_arvalid; // To axe_elink of axi_elink.v + input [31:0] s_axi_awaddr; // To axe_elink of axi_elink.v + input [1:0] s_axi_awburst; // To axe_elink of axi_elink.v + input [3:0] s_axi_awcache; // To axe_elink of axi_elink.v + input [S_IDW-1:0] s_axi_awid; // To axe_elink of axi_elink.v + input [7:0] s_axi_awlen; // To axe_elink of axi_elink.v + input [1:0] s_axi_awlock; // To axe_elink of axi_elink.v + input [2:0] s_axi_awprot; // To axe_elink of axi_elink.v + input [3:0] s_axi_awqos; // To axe_elink of axi_elink.v + input [2:0] s_axi_awsize; // To axe_elink of axi_elink.v + input s_axi_awvalid; // To axe_elink of axi_elink.v + input s_axi_bready; // To axe_elink of axi_elink.v + input s_axi_rready; // To axe_elink of axi_elink.v + input [31:0] s_axi_wdata; // To axe_elink of axi_elink.v + input [S_IDW-1:0] s_axi_wid; // To axe_elink of axi_elink.v + input s_axi_wlast; // To axe_elink of axi_elink.v + input [3:0] s_axi_wstrb; // To axe_elink of axi_elink.v + input s_axi_wvalid; // To axe_elink of axi_elink.v + input sys_clk; // To axe_elink of axi_elink.v + input txi_rd_wait_n; // To axe_elink of axi_elink.v + input txi_rd_wait_p; // To axe_elink of axi_elink.v + input txi_wr_wait_n; // To axe_elink of axi_elink.v + input txi_wr_wait_p; // To axe_elink of axi_elink.v + // End of automatics + + /*AUTOWIRE*/ + axi_elink axe_elink (/*AUTOINST*/ + // Outputs + .elink_active (elink_active), + .rxo_wr_wait_p (rxo_wr_wait_p), + .rxo_wr_wait_n (rxo_wr_wait_n), + .rxo_rd_wait_p (rxo_rd_wait_p), + .rxo_rd_wait_n (rxo_rd_wait_n), + .txo_lclk_p (txo_lclk_p), + .txo_lclk_n (txo_lclk_n), + .txo_frame_p (txo_frame_p), + .txo_frame_n (txo_frame_n), + .txo_data_p (txo_data_p[7:0]), + .txo_data_n (txo_data_n[7:0]), + .chipid (chipid[11:0]), + .chip_resetb (chip_resetb), + .cclk_p (cclk_p), + .cclk_n (cclk_n), + .mailbox_not_empty(mailbox_not_empty), + .mailbox_full (mailbox_full), + .m_axi_awid (m_axi_awid[M_IDW-1:0]), + .m_axi_awaddr (m_axi_awaddr[31:0]), + .m_axi_awlen (m_axi_awlen[7:0]), + .m_axi_awsize (m_axi_awsize[2:0]), + .m_axi_awburst (m_axi_awburst[1:0]), + .m_axi_awlock (m_axi_awlock[1:0]), + .m_axi_awcache (m_axi_awcache[3:0]), + .m_axi_awprot (m_axi_awprot[2:0]), + .m_axi_awqos (m_axi_awqos[3:0]), + .m_axi_awvalid (m_axi_awvalid), + .m_axi_wid (m_axi_wid[M_IDW-1:0]), + .m_axi_wdata (m_axi_wdata[63:0]), + .m_axi_wstrb (m_axi_wstrb[7:0]), + .m_axi_wlast (m_axi_wlast), + .m_axi_wvalid (m_axi_wvalid), + .m_axi_bready (m_axi_bready), + .m_axi_arid (m_axi_arid[M_IDW-1:0]), + .m_axi_araddr (m_axi_araddr[31:0]), + .m_axi_arlen (m_axi_arlen[7:0]), + .m_axi_arsize (m_axi_arsize[2:0]), + .m_axi_arburst (m_axi_arburst[1:0]), + .m_axi_arlock (m_axi_arlock[1:0]), + .m_axi_arcache (m_axi_arcache[3:0]), + .m_axi_arprot (m_axi_arprot[2:0]), + .m_axi_arqos (m_axi_arqos[3:0]), + .m_axi_arvalid (m_axi_arvalid), + .m_axi_rready (m_axi_rready), + .s_axi_arready (s_axi_arready), + .s_axi_awready (s_axi_awready), + .s_axi_bid (s_axi_bid[S_IDW-1:0]), + .s_axi_bresp (s_axi_bresp[1:0]), + .s_axi_bvalid (s_axi_bvalid), + .s_axi_rid (s_axi_rid[S_IDW-1:0]), + .s_axi_rdata (s_axi_rdata[31:0]), + .s_axi_rlast (s_axi_rlast), + .s_axi_rresp (s_axi_rresp[1:0]), + .s_axi_rvalid (s_axi_rvalid), + .s_axi_wready (s_axi_wready), + .timeout (timeout), + // Inputs + .reset (reset), + .sys_clk (sys_clk), + .rxi_lclk_p (rxi_lclk_p), + .rxi_lclk_n (rxi_lclk_n), + .rxi_frame_p (rxi_frame_p), + .rxi_frame_n (rxi_frame_n), + .rxi_data_p (rxi_data_p[7:0]), + .rxi_data_n (rxi_data_n[7:0]), + .txi_wr_wait_p (txi_wr_wait_p), + .txi_wr_wait_n (txi_wr_wait_n), + .txi_rd_wait_p (txi_rd_wait_p), + .txi_rd_wait_n (txi_rd_wait_n), + .m_axi_aresetn (m_axi_aresetn), + .m_axi_awready (m_axi_awready), + .m_axi_wready (m_axi_wready), + .m_axi_bid (m_axi_bid[M_IDW-1:0]), + .m_axi_bresp (m_axi_bresp[1:0]), + .m_axi_bvalid (m_axi_bvalid), + .m_axi_arready (m_axi_arready), + .m_axi_rid (m_axi_rid[M_IDW-1:0]), + .m_axi_rdata (m_axi_rdata[63:0]), + .m_axi_rresp (m_axi_rresp[1:0]), + .m_axi_rlast (m_axi_rlast), + .m_axi_rvalid (m_axi_rvalid), + .s_axi_aresetn (s_axi_aresetn), + .s_axi_arid (s_axi_arid[S_IDW-1:0]), + .s_axi_araddr (s_axi_araddr[31:0]), + .s_axi_arburst (s_axi_arburst[1:0]), + .s_axi_arcache (s_axi_arcache[3:0]), + .s_axi_arlock (s_axi_arlock[1:0]), + .s_axi_arlen (s_axi_arlen[7:0]), + .s_axi_arprot (s_axi_arprot[2:0]), + .s_axi_arqos (s_axi_arqos[3:0]), + .s_axi_arsize (s_axi_arsize[2:0]), + .s_axi_arvalid (s_axi_arvalid), + .s_axi_awid (s_axi_awid[S_IDW-1:0]), + .s_axi_awaddr (s_axi_awaddr[31:0]), + .s_axi_awburst (s_axi_awburst[1:0]), + .s_axi_awcache (s_axi_awcache[3:0]), + .s_axi_awlock (s_axi_awlock[1:0]), + .s_axi_awlen (s_axi_awlen[7:0]), + .s_axi_awprot (s_axi_awprot[2:0]), + .s_axi_awqos (s_axi_awqos[3:0]), + .s_axi_awsize (s_axi_awsize[2:0]), + .s_axi_awvalid (s_axi_awvalid), + .s_axi_bready (s_axi_bready), + .s_axi_rready (s_axi_rready), + .s_axi_wid (s_axi_wid[S_IDW-1:0]), + .s_axi_wdata (s_axi_wdata[31:0]), + .s_axi_wlast (s_axi_wlast), + .s_axi_wstrb (s_axi_wstrb[3:0]), + .s_axi_wvalid (s_axi_wvalid)); + + pgpio pgpio (/*AUTOINST*/ + // Outputs + .ps_gpio_i (ps_gpio_i[NPS-1:0]), + // Inouts + .gpio_p (gpio_p[NGPIO-1:0]), + .gpio_n (gpio_n[NGPIO-1:0]), + // Inputs + .ps_gpio_o (ps_gpio_o[NPS-1:0]), + .ps_gpio_t (ps_gpio_t[NPS-1:0])); + + pi2c pi2c (/*AUTOINST*/ + // Outputs + .i2c_sda_i (i2c_sda_i), + .i2c_scl_i (i2c_scl_i), + // Inouts + .i2c_sda (i2c_sda), + .i2c_scl (i2c_scl), + // Inputs + .i2c_sda_o (i2c_sda_o), + .i2c_sda_t (i2c_sda_t), + .i2c_scl_o (i2c_scl_o), + .i2c_scl_t (i2c_scl_t)); + + + +endmodule // parallella_generic +// Local Variables: +// verilog-library-directories:("." "../../elink/hdl") +// End: + +/* + Copyright (C) 2015 Adapteva, Inc. + + Contributed by Andreas Olofsson + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version.This program is distributed in the hope + that it will be useful,but WITHOUT ANY WARRANTY without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy + of the GNU General Public License along with this program (see the file + COPYING). If not, see . + */ diff --git a/parallella/hdl/pgpio.v b/parallella/hdl/pgpio.v new file mode 100644 index 0000000..47b29e8 --- /dev/null +++ b/parallella/hdl/pgpio.v @@ -0,0 +1,136 @@ + +// Implements GPIO pins from the PS/EMIO +// Works with 7010 (24 pins) or 7020 (48 pins) and +// either single-ended or differential IO +module pgpio(/*AUTOARG*/ + // Outputs + ps_gpio_i, + // Inouts + gpio_p, gpio_n, + // Inputs + ps_gpio_o, ps_gpio_t + ); + + parameter NGPIO = 24; // 12 or 24 + parameter NPS = 64; // signals for PS + parameter DIFF = 0; // 0= single ended + // 1= differential + + inout [NGPIO-1:0] gpio_p; + inout [NGPIO-1:0] gpio_n; + + output [NPS-1:0] ps_gpio_i; + input [NPS-1:0] ps_gpio_o; + input [NPS-1:0] ps_gpio_t; + + genvar m; + + generate + if( DIFF == 1 ) begin: gpio_diff + IOBUFDS + #( + .DIFF_TERM("TRUE"), + .IBUF_LOW_PWR("TRUE"), + .IOSTANDARD("LVDS_25"), + .SLEW("FAST") + ) + i_iodiff [NGPIO-1:0] + ( + .O(ps_gpio_i), // Buffer output + .IO(gpio_p), // Diff_p inout (connect directly to top-level port) + .IOB(gpio_n), // Diff_n inout (connect directly to top-level port) + .I(ps_gpio_o), // Buffer input + .T(ps_gpio_t) // 3-state enable input, high=input, low=output + ); + + end else begin: gpio_cmos // single-ended + + wire [NGPIO-1:0] gpio_i_n, gpio_i_p; + wire [NGPIO-1:0] gpio_o_n, gpio_o_p; + wire [NGPIO-1:0] gpio_t_n, gpio_t_p; + + // Map P/N pins to single-ended signals + for(m=0; m. +*/ + diff --git a/parallella/hdl/pi2c.v b/parallella/hdl/pi2c.v new file mode 100644 index 0000000..1950249 --- /dev/null +++ b/parallella/hdl/pi2c.v @@ -0,0 +1,85 @@ +module pi2c (/*AUTOARG*/ + // Outputs + i2c_sda_i, i2c_scl_i, + // Inouts + i2c_sda, i2c_scl, + // Inputs + i2c_sda_o, i2c_sda_t, i2c_scl_o, i2c_scl_t + ); + + parameter PORTABLE = 0; + + input i2c_sda_o; + input i2c_sda_t; + output i2c_sda_i; + + input i2c_scl_o; + input i2c_scl_t; + output i2c_scl_i; + + inout i2c_sda; + inout i2c_scl; + + generate + if(PORTABLE==1) begin + wire i2c_sda = i2c_sda_t ? 1'bz: i2c_sda_o; + wire i2c_sda_i = i2c_sda; + + wire i2c_scl = i2c_scl_t ? 1'bz : i2c_scl_o; + wire i2c_scl_i = i2c_scl; + end + else + begin + IOBUF #( + .DRIVE(8), // Specify the output drive strength + .IBUF_LOW_PWR("TRUE"), // Low Power - "TRUE", High Performance = "FALSE" + .IOSTANDARD("DEFAULT"), // Specify the I/O standard + .SLEW("SLOW") // Specify the output slew rate + ) i_sda ( + .O(i2c_sda_i), // Buffer output + .IO(i2c_sda), // Buffer inout port (connect directly to top-level port) + .I(i2c_sda_o), // Buffer input + .T(i2c_sda_t) // 3-state enable input, high=input, low=output + ); + + IOBUF #( + .DRIVE(8), + .IBUF_LOW_PWR("TRUE"), + .IOSTANDARD("DEFAULT"), + .SLEW("SLOW") + ) i_scl ( + .O(i2c_scl_i), + .IO(i2c_scl), + .I(i2c_scl_o), + .T(i2c_scl_t) + ); + end + endgenerate + +endmodule // pi2c + + + +/* + File: parallella_i2c + + This file is part of the Parallella FPGA Reference Design. + + Copyright (C) 2013-2015 Adapteva, Inc. + Contributed by Fred Huettig + Contributed by Andreas Olofsson + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program (see the file COPYING). If not, see + . +*/