pwm.cpp 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*******************************************************************************
  2. * Copyright (C) 2017, 2018 MINRES Technologies GmbH
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice,
  9. * this list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * 3. Neither the name of the copyright holder nor the names of its contributors
  16. * may be used to endorse or promote products derived from this software
  17. * without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  23. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. * POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. *******************************************************************************/
  32. #include "sysc/SiFive/pwm.h"
  33. #include "scc/utilities.h"
  34. #include "sysc/SiFive/gen/pwm_regs.h"
  35. using namespace sysc;
  36. using namespace sc_core;
  37. pwm::pwm(sc_core::sc_module_name nm)
  38. : sc_core::sc_module(nm)
  39. , tlm_target<>(clk)
  40. , NAMED(clk_i)
  41. , NAMED(rst_i)
  42. , NAMED(cmpgpio_o, 4)
  43. , NAMED(cmpip_o, 4)
  44. , NAMEDD(regs, pwm_regs)
  45. , current_cnt(0)
  46. , last_cnt_update() {
  47. regs->registerResources(*this);
  48. regs->pwmcfg.set_write_cb(
  49. [this](const scc::sc_register<uint32_t> &reg, const uint32_t &data, sc_core::sc_time d) -> bool {
  50. if (d.value()) wait(d);
  51. reg.put(data);
  52. update_counter();
  53. return true;
  54. });
  55. regs->pwmcount.set_write_cb(
  56. [this](const scc::sc_register<uint32_t> &reg, const uint32_t &data, sc_core::sc_time d) -> bool {
  57. if (d.value()) wait(d);
  58. reg.put(data);
  59. update_counter();
  60. current_cnt = data;
  61. clk_remainder = 0.;
  62. return true;
  63. });
  64. regs->pwmcount.set_read_cb([this](const scc::sc_register<uint32_t> &reg, uint32_t &data,
  65. sc_core::sc_time d) -> bool {
  66. auto offset = regs->r_pwmcfg.pwmenalways || regs->r_pwmcfg.pwmenoneshot ? static_cast<int>(get_pulses(d)) : 0;
  67. data = current_cnt + offset;
  68. regs->r_pwmcount.pwmcount = data;
  69. return true;
  70. });
  71. regs->pwms.set_write_cb(
  72. [this](scc::sc_register<uint32_t> &reg, uint32_t data, sc_core::sc_time d) -> bool { return false; });
  73. regs->pwms.set_read_cb([this](const scc::sc_register<uint32_t> &reg, uint32_t &data, sc_core::sc_time d) -> bool {
  74. auto offset = regs->r_pwmcfg.pwmenalways || regs->r_pwmcfg.pwmenoneshot ? static_cast<int>(get_pulses(d)) : 0;
  75. auto cnt = current_cnt + offset;
  76. data = (cnt >> regs->r_pwmcfg.pwmscale) & 0xffff;
  77. regs->r_pwms.pwms = static_cast<uint16_t>(data);
  78. return true;
  79. });
  80. regs->pwmcmp0.set_write_cb(
  81. [this](const scc::sc_register<uint32_t> &reg, const uint32_t &data, sc_core::sc_time d) -> bool {
  82. reg.put(data);
  83. update_counter();
  84. return true;
  85. });
  86. regs->pwmcmp1.set_write_cb(
  87. [this](const scc::sc_register<uint32_t> &reg, const uint32_t &data, sc_core::sc_time d) -> bool {
  88. reg.put(data);
  89. update_counter();
  90. return true;
  91. });
  92. regs->pwmcmp2.set_write_cb(
  93. [this](const scc::sc_register<uint32_t> &reg, const uint32_t &data, sc_core::sc_time d) -> bool {
  94. reg.put(data);
  95. update_counter();
  96. return true;
  97. });
  98. regs->pwmcmp3.set_write_cb(
  99. [this](const scc::sc_register<uint32_t> &reg, const uint32_t &data, sc_core::sc_time d) -> bool {
  100. reg.put(data);
  101. update_counter();
  102. return true;
  103. });
  104. SC_METHOD(clock_cb);
  105. sensitive << clk_i;
  106. SC_METHOD(reset_cb);
  107. sensitive << rst_i;
  108. SC_METHOD(update_counter);
  109. sensitive << update_counter_evt;
  110. dont_initialize();
  111. }
  112. void pwm::clock_cb() {
  113. update_counter();
  114. clk = clk_i.read();
  115. }
  116. pwm::~pwm() = default;
  117. void pwm::reset_cb() {
  118. if (rst_i.read()) {
  119. regs->reset_start();
  120. } else {
  121. regs->reset_stop();
  122. }
  123. }
  124. void pwm::update_counter() {
  125. auto now = sc_time_stamp();
  126. if (now == SC_ZERO_TIME) return;
  127. update_counter_evt.cancel();
  128. if (regs->r_pwmcfg.pwmenalways || regs->r_pwmcfg.pwmenoneshot) {
  129. std::array<bool, 4> pwmcmp_new_ip{false, false, false, false};
  130. auto dpulses = get_pulses(SC_ZERO_TIME);
  131. auto pulses = static_cast<int>(dpulses);
  132. clk_remainder += dpulses - pulses;
  133. if (clk_remainder > 1) {
  134. pulses++;
  135. clk_remainder -= 1.0;
  136. }
  137. if (reset_cnt) {
  138. current_cnt = 0;
  139. reset_cnt = false;
  140. } else if (last_enable)
  141. current_cnt += pulses;
  142. auto pwms = (current_cnt >> regs->r_pwmcfg.pwmscale) & 0xffff;
  143. auto next_trigger_time =
  144. (0xffff - pwms) * (1 << regs->r_pwmcfg.pwmscale) * clk; // next trigger based on wrap around
  145. if (pwms == 0xffff) { // wrap around calculation
  146. reset_cnt = true;
  147. next_trigger_time = clk;
  148. regs->r_pwmcfg.pwmenoneshot = 0;
  149. }
  150. auto pwms0 = (regs->r_pwmcfg.pwmcmp0center && (pwms & 0x8000) == 1) ? pwms ^ 0xffff : pwms;
  151. if (pwms0 >= regs->r_pwmcmp0.pwmcmp0) {
  152. pwmcmp_new_ip[0] = true;
  153. regs->r_pwmcfg.pwmenoneshot = 0;
  154. if (regs->r_pwmcfg.pwmzerocmp) {
  155. reset_cnt = true;
  156. next_trigger_time = clk;
  157. }
  158. } else {
  159. pwmcmp_new_ip[0] = false;
  160. // TODO: add correct calculation for regs->r_pwmcfg.pwmcmpXcenter==1
  161. auto nt = (regs->r_pwmcmp0.pwmcmp0 - pwms0) * (1 << regs->r_pwmcfg.pwmscale) * clk;
  162. next_trigger_time = nt < next_trigger_time ? nt : next_trigger_time;
  163. }
  164. auto pwms1 = (regs->r_pwmcfg.pwmcmp0center && (pwms & 0x8000) == 1) ? pwms ^ 0xffff : pwms;
  165. if (pwms1 >= regs->r_pwmcmp1.pwmcmp0) {
  166. pwmcmp_new_ip[1] = true;
  167. } else {
  168. pwmcmp_new_ip[1] = false;
  169. // TODO: add correct calculation for regs->r_pwmcfg.pwmcmpXcenter==1
  170. auto nt = (regs->r_pwmcmp0.pwmcmp0 - pwms0) * (1 << regs->r_pwmcfg.pwmscale) * clk;
  171. next_trigger_time = nt < next_trigger_time ? nt : next_trigger_time;
  172. }
  173. auto pwms2 = (regs->r_pwmcfg.pwmcmp0center && (pwms & 0x8000) == 1) ? pwms ^ 0xffff : pwms;
  174. if (pwms2 >= regs->r_pwmcmp2.pwmcmp0) {
  175. pwmcmp_new_ip[2] = true;
  176. } else {
  177. pwmcmp_new_ip[2] = false;
  178. // TODO: add correct calculation for regs->r_pwmcfg.pwmcmpXcenter==1
  179. auto nt = (regs->r_pwmcmp0.pwmcmp0 - pwms0) * regs->r_pwmcfg.pwmscale * clk;
  180. next_trigger_time = nt < next_trigger_time ? nt : next_trigger_time;
  181. }
  182. auto pwms3 = (regs->r_pwmcfg.pwmcmp0center && (pwms & 0x8000) == 1) ? pwms ^ 0xffff : pwms;
  183. if (pwms3 >= regs->r_pwmcmp3.pwmcmp0) {
  184. pwmcmp_new_ip[3] = true;
  185. } else {
  186. pwmcmp_new_ip[3] = false;
  187. // TODO: add correct calculation for regs->r_pwmcfg.pwmcmpXcenter==1
  188. auto nt = (regs->r_pwmcmp0.pwmcmp0 - pwms0) * (1 << regs->r_pwmcfg.pwmscale) * clk;
  189. next_trigger_time = nt < next_trigger_time ? nt : next_trigger_time;
  190. }
  191. for (size_t i = 0; i < 4; ++i) {
  192. // write gpio bits depending of gang bit
  193. if (regs->r_pwmcfg & (1 < (24 + i)))
  194. write_cmpgpio(i, pwmcmp_new_ip[i] && !pwmcmp_new_ip[(i + 1) % 4]);
  195. else
  196. write_cmpgpio(i, pwmcmp_new_ip[i]);
  197. // detect rising edge and set ip bit if found
  198. if (!pwmcmp_ip[i] && pwmcmp_new_ip[i]) regs->r_pwmcfg |= 1 << (28 + i);
  199. pwmcmp_ip[i] = pwmcmp_new_ip[i];
  200. }
  201. last_enable = true;
  202. update_counter_evt.notify(next_trigger_time);
  203. } else
  204. last_enable = false;
  205. cmpip_o[0].write(regs->r_pwmcfg.pwmcmp0ip != 0);
  206. cmpip_o[1].write(regs->r_pwmcfg.pwmcmp1ip != 0);
  207. cmpip_o[2].write(regs->r_pwmcfg.pwmcmp2ip != 0);
  208. cmpip_o[3].write(regs->r_pwmcfg.pwmcmp3ip != 0);
  209. last_cnt_update = now;
  210. last_clk = clk;
  211. }
  212. void pwm::write_cmpgpio(size_t index, bool val) {
  213. if (cmpgpio_o[index].get_interface()) {
  214. tlm::tlm_phase phase(tlm::BEGIN_REQ);
  215. tlm::tlm_signal_gp<> gp;
  216. sc_core::sc_time delay(SC_ZERO_TIME);
  217. gp.set_value(val);
  218. cmpgpio_o[index]->nb_transport_fw(gp, phase, delay);
  219. }
  220. }