From 59722f482aec0a3997c7ef5db677a082dac32c04 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20W=C5=82ostowski?= <tomasz.wlostowski@cern.ch>
Date: Wed, 9 Jan 2013 14:26:53 +0100
Subject: [PATCH] wishbone: added wb_simple_pwm PWM controller

Conflicts:

	modules/wishbone/Manifest.py
---
 modules/wishbone/Manifest.py                  |   1 +
 modules/wishbone/wb_simple_pwm/Manifest.py    |   5 +
 .../wishbone/wb_simple_pwm/simple_pwm_wb.vhd  | 372 ++++++++++++++++++
 .../wishbone/wb_simple_pwm/simple_pwm_wb.wb   | 121 ++++++
 .../wb_simple_pwm/simple_pwm_wbgen2_pkg.vhd   | 129 ++++++
 .../wishbone/wb_simple_pwm/wb_simple_pwm.vhd  | 214 ++++++++++
 .../wishbone/wb_simple_pwm/xwb_simple_pwm.vhd | 108 +++++
 7 files changed, 950 insertions(+)
 create mode 100644 modules/wishbone/wb_simple_pwm/Manifest.py
 create mode 100644 modules/wishbone/wb_simple_pwm/simple_pwm_wb.vhd
 create mode 100644 modules/wishbone/wb_simple_pwm/simple_pwm_wb.wb
 create mode 100644 modules/wishbone/wb_simple_pwm/simple_pwm_wbgen2_pkg.vhd
 create mode 100644 modules/wishbone/wb_simple_pwm/wb_simple_pwm.vhd
 create mode 100644 modules/wishbone/wb_simple_pwm/xwb_simple_pwm.vhd

diff --git a/modules/wishbone/Manifest.py b/modules/wishbone/Manifest.py
index 5f2bf672..4cbb021d 100644
--- a/modules/wishbone/Manifest.py
+++ b/modules/wishbone/Manifest.py
@@ -17,6 +17,7 @@ def __helper():
     "wb_clock_crossing",
     "wb_dma",
     "wb_serial_lcd",
+		"wb_simple_pwm",
     "wbgen2"
     ]
   if (target == "altera"): dirs.extend(["wb_pcie"]);
diff --git a/modules/wishbone/wb_simple_pwm/Manifest.py b/modules/wishbone/wb_simple_pwm/Manifest.py
new file mode 100644
index 00000000..e5018a82
--- /dev/null
+++ b/modules/wishbone/wb_simple_pwm/Manifest.py
@@ -0,0 +1,5 @@
+files = ["simple_pwm_wbgen2_pkg.vhd",
+"simple_pwm_wb.vhd",
+"wb_simple_pwm.vhd",
+"xwb_simple_pwm.vhd"
+];
diff --git a/modules/wishbone/wb_simple_pwm/simple_pwm_wb.vhd b/modules/wishbone/wb_simple_pwm/simple_pwm_wb.vhd
new file mode 100644
index 00000000..1db87840
--- /dev/null
+++ b/modules/wishbone/wb_simple_pwm/simple_pwm_wb.vhd
@@ -0,0 +1,372 @@
+---------------------------------------------------------------------------------------
+-- Title          : Wishbone slave core for Simple Pulse Width Modulation Controller
+---------------------------------------------------------------------------------------
+-- File           : simple_pwm_wb.vhd
+-- Author         : auto-generated by wbgen2 from simple_pwm_wb.wb
+-- Created        : Thu Dec 20 11:32:19 2012
+-- Standard       : VHDL'87
+---------------------------------------------------------------------------------------
+-- THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE simple_pwm_wb.wb
+-- DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
+---------------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+use work.spwm_wbgen2_pkg.all;
+
+
+entity simple_pwm_wb is
+  port (
+    rst_n_i                                  : in     std_logic;
+    clk_sys_i                                : in     std_logic;
+    wb_adr_i                                 : in     std_logic_vector(3 downto 0);
+    wb_dat_i                                 : in     std_logic_vector(31 downto 0);
+    wb_dat_o                                 : out    std_logic_vector(31 downto 0);
+    wb_cyc_i                                 : in     std_logic;
+    wb_sel_i                                 : in     std_logic_vector(3 downto 0);
+    wb_stb_i                                 : in     std_logic;
+    wb_we_i                                  : in     std_logic;
+    wb_ack_o                                 : out    std_logic;
+    wb_stall_o                               : out    std_logic;
+    regs_i                                   : in     t_spwm_in_registers;
+    regs_o                                   : out    t_spwm_out_registers
+  );
+end simple_pwm_wb;
+
+architecture syn of simple_pwm_wb is
+
+signal spwm_cr_presc_int                        : std_logic_vector(15 downto 0);
+signal spwm_cr_period_int                       : std_logic_vector(15 downto 0);
+signal ack_sreg                                 : std_logic_vector(9 downto 0);
+signal rddata_reg                               : std_logic_vector(31 downto 0);
+signal wrdata_reg                               : std_logic_vector(31 downto 0);
+signal bwsel_reg                                : std_logic_vector(3 downto 0);
+signal rwaddr_reg                               : std_logic_vector(3 downto 0);
+signal ack_in_progress                          : std_logic      ;
+signal wr_int                                   : std_logic      ;
+signal rd_int                                   : std_logic      ;
+signal allones                                  : std_logic_vector(31 downto 0);
+signal allzeros                                 : std_logic_vector(31 downto 0);
+
+begin
+-- Some internal signals assignments. For (foreseen) compatibility with other bus standards.
+  wrdata_reg <= wb_dat_i;
+  bwsel_reg <= wb_sel_i;
+  rd_int <= wb_cyc_i and (wb_stb_i and (not wb_we_i));
+  wr_int <= wb_cyc_i and (wb_stb_i and wb_we_i);
+  allones <= (others => '1');
+  allzeros <= (others => '0');
+-- 
+-- Main register bank access process.
+  process (clk_sys_i, rst_n_i)
+  begin
+    if (rst_n_i = '0') then 
+      ack_sreg <= "0000000000";
+      ack_in_progress <= '0';
+      rddata_reg <= "00000000000000000000000000000000";
+      spwm_cr_presc_int <= "0000000000000000";
+      spwm_cr_period_int <= "0000000000000000";
+      regs_o.dr0_load_o <= '0';
+      regs_o.dr1_load_o <= '0';
+      regs_o.dr2_load_o <= '0';
+      regs_o.dr3_load_o <= '0';
+      regs_o.dr4_load_o <= '0';
+      regs_o.dr5_load_o <= '0';
+      regs_o.dr6_load_o <= '0';
+      regs_o.dr7_load_o <= '0';
+    elsif rising_edge(clk_sys_i) then
+-- advance the ACK generator shift register
+      ack_sreg(8 downto 0) <= ack_sreg(9 downto 1);
+      ack_sreg(9) <= '0';
+      if (ack_in_progress = '1') then
+        if (ack_sreg(0) = '1') then
+          regs_o.dr0_load_o <= '0';
+          regs_o.dr1_load_o <= '0';
+          regs_o.dr2_load_o <= '0';
+          regs_o.dr3_load_o <= '0';
+          regs_o.dr4_load_o <= '0';
+          regs_o.dr5_load_o <= '0';
+          regs_o.dr6_load_o <= '0';
+          regs_o.dr7_load_o <= '0';
+          ack_in_progress <= '0';
+        else
+          regs_o.dr0_load_o <= '0';
+          regs_o.dr1_load_o <= '0';
+          regs_o.dr2_load_o <= '0';
+          regs_o.dr3_load_o <= '0';
+          regs_o.dr4_load_o <= '0';
+          regs_o.dr5_load_o <= '0';
+          regs_o.dr6_load_o <= '0';
+          regs_o.dr7_load_o <= '0';
+        end if;
+      else
+        if ((wb_cyc_i = '1') and (wb_stb_i = '1')) then
+          case rwaddr_reg(3 downto 0) is
+          when "0000" => 
+            if (wb_we_i = '1') then
+              spwm_cr_presc_int <= wrdata_reg(15 downto 0);
+              spwm_cr_period_int <= wrdata_reg(31 downto 16);
+            end if;
+            rddata_reg(15 downto 0) <= spwm_cr_presc_int;
+            rddata_reg(31 downto 16) <= spwm_cr_period_int;
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0001" => 
+            if (wb_we_i = '1') then
+            end if;
+            rddata_reg(3 downto 0) <= regs_i.sr_n_channels_i;
+            rddata_reg(4) <= 'X';
+            rddata_reg(5) <= 'X';
+            rddata_reg(6) <= 'X';
+            rddata_reg(7) <= 'X';
+            rddata_reg(8) <= 'X';
+            rddata_reg(9) <= 'X';
+            rddata_reg(10) <= 'X';
+            rddata_reg(11) <= 'X';
+            rddata_reg(12) <= 'X';
+            rddata_reg(13) <= 'X';
+            rddata_reg(14) <= 'X';
+            rddata_reg(15) <= 'X';
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0010" => 
+            if (wb_we_i = '1') then
+              regs_o.dr0_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr0_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0011" => 
+            if (wb_we_i = '1') then
+              regs_o.dr1_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr1_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0100" => 
+            if (wb_we_i = '1') then
+              regs_o.dr2_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr2_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0101" => 
+            if (wb_we_i = '1') then
+              regs_o.dr3_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr3_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0110" => 
+            if (wb_we_i = '1') then
+              regs_o.dr4_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr4_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "0111" => 
+            if (wb_we_i = '1') then
+              regs_o.dr5_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr5_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "1000" => 
+            if (wb_we_i = '1') then
+              regs_o.dr6_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr6_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when "1001" => 
+            if (wb_we_i = '1') then
+              regs_o.dr7_load_o <= '1';
+            end if;
+            rddata_reg(15 downto 0) <= regs_i.dr7_i;
+            rddata_reg(16) <= 'X';
+            rddata_reg(17) <= 'X';
+            rddata_reg(18) <= 'X';
+            rddata_reg(19) <= 'X';
+            rddata_reg(20) <= 'X';
+            rddata_reg(21) <= 'X';
+            rddata_reg(22) <= 'X';
+            rddata_reg(23) <= 'X';
+            rddata_reg(24) <= 'X';
+            rddata_reg(25) <= 'X';
+            rddata_reg(26) <= 'X';
+            rddata_reg(27) <= 'X';
+            rddata_reg(28) <= 'X';
+            rddata_reg(29) <= 'X';
+            rddata_reg(30) <= 'X';
+            rddata_reg(31) <= 'X';
+            ack_sreg(0) <= '1';
+            ack_in_progress <= '1';
+          when others =>
+-- prevent the slave from hanging the bus on invalid address
+            ack_in_progress <= '1';
+            ack_sreg(0) <= '1';
+          end case;
+        end if;
+      end if;
+    end if;
+  end process;
+  
+  
+-- Drive the data output bus
+  wb_dat_o <= rddata_reg;
+-- Prescaler Ratio
+  regs_o.cr_presc_o <= spwm_cr_presc_int;
+-- Period
+  regs_o.cr_period_o <= spwm_cr_period_int;
+-- Channel count
+-- Value
+  regs_o.dr0_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr1_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr2_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr3_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr4_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr5_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr6_o <= wrdata_reg(15 downto 0);
+-- Value
+  regs_o.dr7_o <= wrdata_reg(15 downto 0);
+  rwaddr_reg <= wb_adr_i;
+  wb_stall_o <= (not ack_sreg(0)) and (wb_stb_i and wb_cyc_i);
+-- ACK signal generation. Just pass the LSB of ACK counter.
+  wb_ack_o <= ack_sreg(0);
+end syn;
diff --git a/modules/wishbone/wb_simple_pwm/simple_pwm_wb.wb b/modules/wishbone/wb_simple_pwm/simple_pwm_wb.wb
new file mode 100644
index 00000000..d73b5c90
--- /dev/null
+++ b/modules/wishbone/wb_simple_pwm/simple_pwm_wb.wb
@@ -0,0 +1,121 @@
+-- -*- Mode: LUA; tab-width: 2 -*-
+
+-------------------------------------------------------------------------------
+-- Title      : Simple PWM controller
+-- Project    : General Cores Collection
+-------------------------------------------------------------------------------
+-- File       : simple_pwm_wb.wb
+-- Author     : Tomasz WÅ‚ostowski
+-- Company    : CERN BE-CO-HT
+-- Created    : 2012-12-10
+-- Last update: 2013-01-09
+-------------------------------------------------------------------------------
+-- Description: A simple, multichannel PWM controller (register layout)
+-------------------------------------------------------------------------------
+--
+-- Copyright (c) 2013 CERN / BE-CO-HT
+--
+-- This source file is free software; you can redistribute it   
+-- and/or modify it under the terms of the GNU Lesser General   
+-- Public License as published by the Free Software Foundation; 
+-- either version 2.1 of the License, or (at your option) any   
+-- later version.                                               
+--
+-- This source 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 Lesser General Public License for more 
+-- details.                                                     
+--
+-- You should have received a copy of the GNU Lesser General    
+-- Public License along with this source; if not, download it   
+-- from http://www.gnu.org/licenses/lgpl-2.1l.html
+--
+-------------------------------------------------------------------------------
+peripheral {
+    name = "Simple Pulse Width Modulation Controller";
+    description = "A very simple multichannel PWM controller.";
+    prefix = "spwm";
+    hdl_entity = "simple_pwm_wb";
+
+   reg {
+			name = "Control Register";
+			prefix = "CR";
+			
+			field {
+				 name = "Prescaler Ratio";
+				 description = "PWM Base clock prescaler. Divides the system clock to obtain the PWM counter clock. The division ratio is (PRESC + 1).";
+				 type = SLV;
+         size = 16;
+         prefix = "PRESC";
+				 access_bus = READ_WRITE;
+				 access_dev = READ_ONLY;
+			};
+
+			field {
+				 name = "Period";
+				 description = "PWM Cycle Period. The real-time period value <code>PERIOD * (PRESC + 1) * t_clk_sys.</code>. Acceptable values: 0..65534.";
+				 type = SLV;
+         size = 16;
+         prefix = "PERIOD";
+				 access_bus = READ_WRITE;
+				 access_dev = READ_ONLY;
+			};
+	 };
+
+
+   reg {
+      name = "Status Register";
+      prefix = "SR";
+      field {
+				 name = "Channel count";
+				 description = "Number of channels supported by this particular implementation, from 1 to 8. ";
+				 type = SLV;
+         size = 4;
+         prefix = "N_CHANNELS";
+				 access_bus = READ_ONLY;
+				 access_dev = WRITE_ONLY;
+         
+      };
+   };
+};
+
+drive_reg_template = 
+	 {
+
+   reg {
+			name = "Channel %d Drive Register";
+			
+			description = "Current PWM duty cycle for channel %d.";
+			prefix = "DR%d";
+			
+      field {
+				 name = "Value";
+				 type = SLV;
+         size = 16;
+				 access_bus = READ_WRITE;
+				 access_dev = READ_WRITE;
+				 load = LOAD_EXT;
+      };
+   };
+};
+
+function generate_dregs(n)
+   local i;
+
+   for i=0,n-1 do
+      local T=deepcopy(drive_reg_template);
+			
+			
+      foreach_reg({TYPE_REG}, function(r)
+																 r.name = string.format(r.name, i);
+																 r.prefix = string.format(r.prefix, i);
+																 print(r.name)
+															end, T);
+
+
+      table_join(periph, T);
+   end
+end
+
+generate_dregs(8);
diff --git a/modules/wishbone/wb_simple_pwm/simple_pwm_wbgen2_pkg.vhd b/modules/wishbone/wb_simple_pwm/simple_pwm_wbgen2_pkg.vhd
new file mode 100644
index 00000000..ab39ae28
--- /dev/null
+++ b/modules/wishbone/wb_simple_pwm/simple_pwm_wbgen2_pkg.vhd
@@ -0,0 +1,129 @@
+---------------------------------------------------------------------------------------
+-- Title          : Wishbone slave core for Simple Pulse Width Modulation Controller
+---------------------------------------------------------------------------------------
+-- File           : simple_pwm_wbgen2_pkg.vhd
+-- Author         : auto-generated by wbgen2 from simple_pwm_wb.wb
+-- Created        : Thu Dec 20 11:32:19 2012
+-- Standard       : VHDL'87
+---------------------------------------------------------------------------------------
+-- THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE simple_pwm_wb.wb
+-- DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
+---------------------------------------------------------------------------------------
+
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+
+package spwm_wbgen2_pkg is
+  
+  
+  -- Input registers (user design -> WB slave)
+  
+  type t_spwm_in_registers is record
+    sr_n_channels_i                          : std_logic_vector(3 downto 0);
+    dr0_i                                    : std_logic_vector(15 downto 0);
+    dr1_i                                    : std_logic_vector(15 downto 0);
+    dr2_i                                    : std_logic_vector(15 downto 0);
+    dr3_i                                    : std_logic_vector(15 downto 0);
+    dr4_i                                    : std_logic_vector(15 downto 0);
+    dr5_i                                    : std_logic_vector(15 downto 0);
+    dr6_i                                    : std_logic_vector(15 downto 0);
+    dr7_i                                    : std_logic_vector(15 downto 0);
+    end record;
+  
+  constant c_spwm_in_registers_init_value: t_spwm_in_registers := (
+    sr_n_channels_i => (others => '0'),
+    dr0_i => (others => '0'),
+    dr1_i => (others => '0'),
+    dr2_i => (others => '0'),
+    dr3_i => (others => '0'),
+    dr4_i => (others => '0'),
+    dr5_i => (others => '0'),
+    dr6_i => (others => '0'),
+    dr7_i => (others => '0')
+    );
+    
+    -- Output registers (WB slave -> user design)
+    
+    type t_spwm_out_registers is record
+      cr_presc_o                               : std_logic_vector(15 downto 0);
+      cr_period_o                              : std_logic_vector(15 downto 0);
+      dr0_o                                    : std_logic_vector(15 downto 0);
+      dr0_load_o                               : std_logic;
+      dr1_o                                    : std_logic_vector(15 downto 0);
+      dr1_load_o                               : std_logic;
+      dr2_o                                    : std_logic_vector(15 downto 0);
+      dr2_load_o                               : std_logic;
+      dr3_o                                    : std_logic_vector(15 downto 0);
+      dr3_load_o                               : std_logic;
+      dr4_o                                    : std_logic_vector(15 downto 0);
+      dr4_load_o                               : std_logic;
+      dr5_o                                    : std_logic_vector(15 downto 0);
+      dr5_load_o                               : std_logic;
+      dr6_o                                    : std_logic_vector(15 downto 0);
+      dr6_load_o                               : std_logic;
+      dr7_o                                    : std_logic_vector(15 downto 0);
+      dr7_load_o                               : std_logic;
+      end record;
+    
+    constant c_spwm_out_registers_init_value: t_spwm_out_registers := (
+      cr_presc_o => (others => '0'),
+      cr_period_o => (others => '0'),
+      dr0_o => (others => '0'),
+      dr0_load_o => '0',
+      dr1_o => (others => '0'),
+      dr1_load_o => '0',
+      dr2_o => (others => '0'),
+      dr2_load_o => '0',
+      dr3_o => (others => '0'),
+      dr3_load_o => '0',
+      dr4_o => (others => '0'),
+      dr4_load_o => '0',
+      dr5_o => (others => '0'),
+      dr5_load_o => '0',
+      dr6_o => (others => '0'),
+      dr6_load_o => '0',
+      dr7_o => (others => '0'),
+      dr7_load_o => '0'
+      );
+    function "or" (left, right: t_spwm_in_registers) return t_spwm_in_registers;
+    function f_x_to_zero (x:std_logic) return std_logic;
+    function f_x_to_zero (x:std_logic_vector) return std_logic_vector;
+end package;
+
+package body spwm_wbgen2_pkg is
+function f_x_to_zero (x:std_logic) return std_logic is
+begin
+if(x = 'X' or x = 'U') then
+return '0';
+else
+return x;
+end if; 
+end function;
+function f_x_to_zero (x:std_logic_vector) return std_logic_vector is
+variable tmp: std_logic_vector(x'length-1 downto 0);
+begin
+for i in 0 to x'length-1 loop
+if(x(i) = 'X' or x(i) = 'U') then
+tmp(i):= '0';
+else
+tmp(i):=x(i);
+end if; 
+end loop; 
+return tmp;
+end function;
+function "or" (left, right: t_spwm_in_registers) return t_spwm_in_registers is
+variable tmp: t_spwm_in_registers;
+begin
+tmp.sr_n_channels_i := f_x_to_zero(left.sr_n_channels_i) or f_x_to_zero(right.sr_n_channels_i);
+tmp.dr0_i := f_x_to_zero(left.dr0_i) or f_x_to_zero(right.dr0_i);
+tmp.dr1_i := f_x_to_zero(left.dr1_i) or f_x_to_zero(right.dr1_i);
+tmp.dr2_i := f_x_to_zero(left.dr2_i) or f_x_to_zero(right.dr2_i);
+tmp.dr3_i := f_x_to_zero(left.dr3_i) or f_x_to_zero(right.dr3_i);
+tmp.dr4_i := f_x_to_zero(left.dr4_i) or f_x_to_zero(right.dr4_i);
+tmp.dr5_i := f_x_to_zero(left.dr5_i) or f_x_to_zero(right.dr5_i);
+tmp.dr6_i := f_x_to_zero(left.dr6_i) or f_x_to_zero(right.dr6_i);
+tmp.dr7_i := f_x_to_zero(left.dr7_i) or f_x_to_zero(right.dr7_i);
+return tmp;
+end function;
+end package body;
diff --git a/modules/wishbone/wb_simple_pwm/wb_simple_pwm.vhd b/modules/wishbone/wb_simple_pwm/wb_simple_pwm.vhd
new file mode 100644
index 00000000..ac26be86
--- /dev/null
+++ b/modules/wishbone/wb_simple_pwm/wb_simple_pwm.vhd
@@ -0,0 +1,214 @@
+-------------------------------------------------------------------------------
+-- Title      : Simple Wishbone PWM Controller
+-- Project    : General Cores Collection (gencores) library
+-------------------------------------------------------------------------------
+-- File       : wb_simple_pwm.vhd
+-- Author     : Tomasz Wlostowski
+-- Company    : CERN BE-Co-HT
+-- Created    : 2012-12-18
+-- Last update: 2012-12-20
+-- Platform   : FPGA-generic
+-- Standard   : VHDL'93
+-------------------------------------------------------------------------------
+-- Description: A simple PWM controller, supporting up to 8 channels. Aside from
+-- duty cycle control, all channels share period and base frequency settings,
+-- contrillable via Wishbone
+-------------------------------------------------------------------------------
+--
+-- This source file is free software; you can redistribute it   
+-- and/or modify it under the terms of the GNU Lesser General   
+-- Public License as published by the Free Software Foundation; 
+-- either version 2.1 of the License, or (at your option) any   
+-- later version.                                               
+--
+-- This source 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 Lesser General Public License for more 
+-- details.                                                     
+--
+-- You should have received a copy of the GNU Lesser General    
+-- Public License along with this source; if not, download it   
+-- from http://www.gnu.org/licenses/lgpl-2.1.html
+--
+-------------------------------------------------------------------------------
+
+library ieee;
+use ieee.STD_LOGIC_1164.all;
+use ieee.NUMERIC_STD.all;
+
+use work.wishbone_pkg.all;
+use work.spwm_wbgen2_pkg.all;
+
+entity wb_simple_pwm is
+  generic(
+    g_num_channels        : integer range 1 to 8;
+    g_interface_mode      : t_wishbone_interface_mode      := PIPELINED;
+    g_address_granularity : t_wishbone_address_granularity := BYTE
+    );
+  port (
+
+    clk_sys_i : in std_logic;
+    rst_n_i   : in std_logic;
+
+    wb_adr_i   : in  std_logic_vector(5 downto 0);
+    wb_dat_i   : in  std_logic_vector(31 downto 0);
+    wb_dat_o   : out std_logic_vector(31 downto 0);
+    wb_cyc_i   : in  std_logic;
+    wb_sel_i   : in  std_logic_vector(3 downto 0);
+    wb_stb_i   : in  std_logic;
+    wb_we_i    : in  std_logic;
+    wb_ack_o   : out std_logic;
+    wb_stall_o : out std_logic;
+
+    pwm_o : out std_logic_vector(g_num_channels-1 downto 0)
+    );
+end wb_simple_pwm;
+
+architecture behavioral of wb_simple_pwm is
+
+  type t_drive_array is array(0 to g_num_channels-1) of std_logic_vector(15 downto 0);
+
+  procedure f_handle_dr_writes(index :     integer; data : std_logic_vector; load : std_logic;
+  signal d                           : out t_drive_array) is
+  begin
+    if(index <= g_num_channels and load = '1') then
+      d(index-1) <= data;
+    end if;
+  end f_handle_dr_writes;
+
+  component simple_pwm_wb
+    port (
+      rst_n_i    : in  std_logic;
+      clk_sys_i  : in  std_logic;
+      wb_adr_i   : in  std_logic_vector(3 downto 0);
+      wb_dat_i   : in  std_logic_vector(31 downto 0);
+      wb_dat_o   : out std_logic_vector(31 downto 0);
+      wb_cyc_i   : in  std_logic;
+      wb_sel_i   : in  std_logic_vector(3 downto 0);
+      wb_stb_i   : in  std_logic;
+      wb_we_i    : in  std_logic;
+      wb_ack_o   : out std_logic;
+      wb_stall_o : out std_logic;
+      regs_i     : in  t_spwm_in_registers;
+      regs_o     : out t_spwm_out_registers);
+  end component;
+
+  signal drive : t_drive_array;
+
+  signal regs_in  : t_spwm_in_registers;
+  signal regs_out : t_spwm_out_registers;
+
+  signal tick                : std_logic;
+  signal cntr_pre, cntr_main : unsigned(15 downto 0);
+  
+begin  -- behavioral
+
+  U_WB_Slave : simple_pwm_wb
+    port map (
+      rst_n_i    => rst_n_i,
+      clk_sys_i  => clk_sys_i,
+      wb_adr_i   => wb_adr_i(5 downto 2),
+      wb_dat_i   => wb_dat_i,
+      wb_dat_o   => wb_dat_o,
+      wb_cyc_i   => wb_cyc_i,
+      wb_sel_i   => wb_sel_i,
+      wb_stb_i   => wb_stb_i,
+      wb_we_i    => wb_we_i,
+      wb_ack_o   => wb_ack_o,
+      wb_stall_o => wb_stall_o,
+      regs_i     => regs_in,
+      regs_o     => regs_out);
+
+-------------------------------------------------------------------------------
+-- FIXME: this is ugly. Add register array support in wbgen2!
+-------------------------------------------------------------------------------
+  p_drive_readback : process(drive)
+  begin
+    regs_in.dr0_i <= drive(0);
+
+    if(g_num_channels >= 2) then
+      regs_in.dr1_i <= drive(1);
+    end if;
+    if(g_num_channels >= 3) then
+      regs_in.dr2_i <= drive(2);
+    end if;
+    if(g_num_channels >= 4) then
+      regs_in.dr3_i <= drive(3);
+    end if;
+    if(g_num_channels >= 5) then
+      regs_in.dr4_i <= drive(4);
+    end if;
+    if(g_num_channels >= 6) then
+      regs_in.dr5_i <= drive(5);
+    end if;
+    if(g_num_channels >= 7) then
+      regs_in.dr6_i <= drive(6);
+    end if;
+    if(g_num_channels >= 8) then
+      regs_in.dr7_i <= drive(7);
+    end if;
+  end process;
+
+  p_drive_write : process(clk_sys_i)
+  begin
+    if rising_edge(clk_sys_i) then
+      if rst_n_i = '0' then
+        for i in 0 to g_num_channels-1 loop
+          drive(i) <= (others => '0');
+        end loop;  -- i
+      else
+        f_handle_dr_writes(1, regs_out.dr0_o, regs_out.dr0_load_o, drive);
+        f_handle_dr_writes(2, regs_out.dr1_o, regs_out.dr1_load_o, drive);
+        f_handle_dr_writes(3, regs_out.dr2_o, regs_out.dr2_load_o, drive);
+        f_handle_dr_writes(4, regs_out.dr3_o, regs_out.dr3_load_o, drive);
+        f_handle_dr_writes(5, regs_out.dr4_o, regs_out.dr4_load_o, drive);
+        f_handle_dr_writes(6, regs_out.dr5_o, regs_out.dr5_load_o, drive);
+        f_handle_dr_writes(7, regs_out.dr6_o, regs_out.dr6_load_o, drive);
+        f_handle_dr_writes(8, regs_out.dr7_o, regs_out.dr7_load_o, drive);
+      end if;
+    end if;
+  end process;
+
+  p_prescaler : process(clk_sys_i)
+  begin
+    if rising_edge(clk_sys_i) then
+      if rst_n_i = '0' or std_logic_vector(cntr_pre) = regs_out.cr_presc_o then
+        cntr_pre <= (others => '0');
+        tick     <= '1';
+      else
+        cntr_pre <= cntr_pre + 1;
+        tick     <= '0';
+      end if;
+    end if;
+  end process;
+
+  p_main_counter : process(clk_sys_i)
+  begin
+    if rising_edge(clk_sys_i) then
+      if (tick = '1' and std_logic_vector(cntr_main) = regs_out.cr_period_o) or rst_n_i = '0' then
+        cntr_main <= (others => '0');
+      elsif(tick = '1') then
+        cntr_main <= cntr_main + 1;
+      end if;
+    end if;
+  end process;
+
+  p_comparators : process(clk_sys_i)
+  begin
+    if rising_edge(clk_sys_i) then
+      if rst_n_i = '0' then
+        pwm_o <= (others => '0');
+      else
+        for i in 0 to g_num_channels-1 loop
+          if(cntr_main < unsigned(drive(i))) then
+            pwm_o(i) <= '1';
+          else
+            pwm_o(i) <= '0';
+          end if;
+        end loop;  -- i 
+      end if;
+    end if;
+  end process;
+
+end behavioral;
diff --git a/modules/wishbone/wb_simple_pwm/xwb_simple_pwm.vhd b/modules/wishbone/wb_simple_pwm/xwb_simple_pwm.vhd
new file mode 100644
index 00000000..3ae6ed87
--- /dev/null
+++ b/modules/wishbone/wb_simple_pwm/xwb_simple_pwm.vhd
@@ -0,0 +1,108 @@
+-------------------------------------------------------------------------------
+-- Title      : Simple Wishbone PWM Controller
+-- Project    : General Cores Collection (gencores) library
+-------------------------------------------------------------------------------
+-- File       : xwb_simple_pwm.vhd
+-- Author     : Tomasz Wlostowski
+-- Company    : CERN BE-Co-HT
+-- Created    : 2012-12-18
+-- Last update: 2012-12-20
+-- Platform   : FPGA-generic
+-- Standard   : VHDL'93
+-------------------------------------------------------------------------------
+-- Description: A simple PWM controller, supporting up to 8 channels. Aside from
+-- duty cycle control, all channels share period and base frequency settings,
+-- contrillable via Wishbone
+-------------------------------------------------------------------------------
+--
+-- This source file is free software; you can redistribute it   
+-- and/or modify it under the terms of the GNU Lesser General   
+-- Public License as published by the Free Software Foundation; 
+-- either version 2.1 of the License, or (at your option) any   
+-- later version.                                               
+--
+-- This source 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 Lesser General Public License for more 
+-- details.                                                     
+--
+-- You should have received a copy of the GNU Lesser General    
+-- Public License along with this source; if not, download it   
+-- from http://www.gnu.org/licenses/lgpl-2.1.html
+--
+-------------------------------------------------------------------------------
+library ieee;
+use ieee.std_logic_1164.all;
+
+library work;
+use work.wishbone_pkg.all;
+
+
+
+entity xwb_simple_pwm is
+  generic (
+    g_num_channels        : integer range 1 to 8;
+    g_interface_mode      : t_wishbone_interface_mode      := PIPELINED;
+    g_address_granularity : t_wishbone_address_granularity := BYTE);
+  port(
+    clk_sys_i : in std_logic;
+    rst_n_i   : in std_logic;
+
+    -- Wishbone
+    slave_i : in  t_wishbone_slave_in;
+    slave_o : out t_wishbone_slave_out;
+
+    pwm_o : out std_logic_vector(g_num_channels-1 downto 0)
+    );
+
+end xwb_simple_pwm;
+
+architecture wrapper of xwb_simple_pwm is
+
+  component wb_simple_pwm
+    generic (
+      g_num_channels        : integer range 1 to 8;
+      g_interface_mode      : t_wishbone_interface_mode;
+      g_address_granularity : t_wishbone_address_granularity);
+    port (
+      clk_sys_i  : in  std_logic;
+      rst_n_i    : in  std_logic;
+      wb_adr_i   : in  std_logic_vector(5 downto 0);
+      wb_dat_i   : in  std_logic_vector(31 downto 0);
+      wb_dat_o   : out std_logic_vector(31 downto 0);
+      wb_cyc_i   : in  std_logic;
+      wb_sel_i   : in  std_logic_vector(3 downto 0);
+      wb_stb_i   : in  std_logic;
+      wb_we_i    : in  std_logic;
+      wb_ack_o   : out std_logic;
+      wb_stall_o : out std_logic;
+      pwm_o      : out std_logic_vector(g_num_channels-1 downto 0));
+  end component;
+  
+begin  -- rtl
+
+  U_Wrapped_PWM : wb_simple_pwm
+    generic map (
+      g_num_channels        => g_num_channels,
+      g_interface_mode      => g_interface_mode,
+      g_address_granularity => g_address_granularity)
+    port map (
+      clk_sys_i  => clk_sys_i,
+      rst_n_i    => rst_n_i,
+      wb_adr_i   => slave_i.adr(5 downto 0),
+      wb_dat_i   => slave_i.dat,
+      wb_dat_o   => slave_o.dat,
+      wb_cyc_i   => slave_i.cyc,
+      wb_sel_i   => slave_i.sel,
+      wb_stb_i   => slave_i.stb,
+      wb_we_i    => slave_i.we,
+      wb_ack_o   => slave_o.ack,
+      wb_stall_o => slave_o.stall,
+      pwm_o      => pwm_o);
+
+  slave_o.err <= '0';
+  slave_o.rty <= '0';
+  slave_o.int <= '0';
+  
+end wrapper;
-- 
GitLab